字节跳动秋招面试后台岗位

笔试5个题目,比较幸运一次过了。大概作了2.5的样子。html

一面:40分钟左右java

编程题目:mysql

1.给出一个有序数组的翻转数组,如何求到原数组的中位数面试

剑指offer上有个题目是给出有序数组的翻转数组,而后求其最小值。可使用二分的方式解。算法

此题我当时给出的答案是sql

a.找到翻转点,而后还原原数组,天然求得中位数。时间复杂度o(n) ,空间复杂度o(n),在这个思路上能够进行优化,能够不使用额外的空间。数据库

b.能够用排序算法求出原数组,时间复杂度o(nlogn),可是没用到翻转数组的特性编程

c.因为其实问题就是求第k个位置的元素是多少,能够借助快排的partition来辅助,指望时间复杂度o(n)。一样没用到翻转数组的特性。数组

2.写一个线程安全的单例模式缓存

https://blog.csdn.net/u011010851/article/details/79873058

饿汉式:由于很饿,因此一开始就把对象new好了。对象,构造函数私有。提供public的get方法。是线程安全的。

懒汉式:有人调用的时候才new。对象,构造函数私有。提供public的get方法。线程非安全,须要把get方法定义成sychronized或者使用同步代码块(双检锁)。

面试中未回答的好的问题

1.http报文信息

https://www.cnblogs.com/jiu0821/p/5641600.html

请求行/请求头/空白行/请求体

2.进程调度都有哪些算法

https://www.cnblogs.com/Blog-day/p/My_Blog_Days1-11.html

先来先服务;短做业优先;高优先级先执行;高响应比优先;时间片轮转;多级反馈队列调度

二面:40分钟左右

编程题目:

1.单链表的原地逆序

a.采用递归的方式写

public static ListNode reorder1(ListNode root){
        if (root == null){
            return null;
        }else if (root.next == null){
            return root;
        }
        ListNode next = reorder(root.next);
        root.next.next = root;
        root.next = null;
        return next;
    }

b.采用指针的方式进行处理

public static ListNode reorder(ListNode root){
        if (root == null){
            return null;
        }
        ListNode pre = null;
        ListNode now = root;
        ListNode next = root.next;
        while (next!=null){
            now.next = pre;
            pre = now;
            now = next;
            next = next.next;
        }
        now.next = pre;
        return now;
    }

c.非原地的方法就是借助栈

面试中未回答的好的问题

1.索引应该创建在怎样的列上

https://blog.csdn.net/ahhsxy/article/details/5458629?utm_source=jiancool

不该该创建索引的列:在查询中比较少使用;只有少许数据值;定义为text,image,bit的列不该该使用;修改的性能远远大于检索的性能;

2.四种引用类型

http://www.cnblogs.com/yaowen/p/6292984.html

强引用:当咱们使用new建立对象时,被建立的对象就是强引用,如Object object = new Object(),其中的object就是一个强引用了。若是一个对象具备强引用,JVM就不会去GC它,JVM宁肯会报OOM来终止程序,也不回收该对象。 

软引用: 若是一个对象只具有软引用,若是内存空间足够,那么JVM就不会GC它,若是内存空间不足了,就会GC该对象。 

弱引用: 若是一个对象只具备弱引用,只要JVM的GC线程检测到了,就会当即回收。弱引用的生命周期要比软引用短不少。不过,若是垃圾回收器是一个优先级很低的线程,也不必定会很快就会释放掉软引用的内存。 

虚引用:若是一个对象只具备虚引用,那么它就和没有任何引用同样,随时会被JVM看成垃圾进行GC。 

3.数据库的4种隔离级别

http://www.javashuo.com/article/p-xisokohk-bp.html

首先要解释脏读,不可重复读,幻读

事务隔离级别 脏读 不可重复读 幻读
读未提交(read-uncommitted)
不可重复读(read-committed)MMUC
可重复读(repeatable-read)MMUC
串行化(serializable)

4.信号量机制

https://blog.csdn.net/lierming__/article/details/78986679

经过pv操做来进行互斥和同步。有相似整形信号量,记录型信号量等;

5.路由器分段和重组

https://blog.csdn.net/hanzhen7541/article/details/79031781

对于不一样的网络,其中传送的包大小可能不同,所以把大包分小的功能是必须的。分段是将数据分组分割成小块以便它们可以适合基础网络的帧。数据报也能够被标记为“不可分段”,若是一个数据报被标记了,那么在任何状况下都不许对它进行分段。若是不分段到不了目的地,那就把包在半路抛弃了。在本地网内进行的从新分段和重组对IP模块是不可见的,这种方法也可使用。

透明分段原则是:当包遇到了通不过的子网的时候,在进入以前由路由器按照子网的MTU(最大传输单元)进行分段,前面的分段对于后面的网络透明,离开子网的时候重组通过分段的包。

不透明分段:在任何中间网关都不进行重组,必要的时候只进行分段,仅仅在目标主机进行重组。

3面:3面问的很是专业和细节,颇有难度,对我颇有帮助,不少问题会开放性的问下若是是你会如何设计(然而并答很差)

编程题:

题目很简单,可是提到了两个细节问题,不专业的写出来的代码虽然功能Ok,可是面试官一问我发现之前有不少我没考虑到的问题。

1.报错机制应该如何设计

a.经过返回值来通知用户是否出错。这种方法会致使没法直接返回返回值。

b.发生错误时设置一个全局变量,则函数能够直接传递返回值;而后设置一个函数去分析这个全局变量。存在的问题就是常常忘记检查全局变量。

c.异常:在发生错误时抛出异常,而且能够根据错误的缘由抛出对应的异常

2.实例化类中资源时,应该写在什么地方

因为在实例化类中资源时为了防止有报错的状况,因此最好放在构造函数中

面试中未回答的好的问题

1.java中对锁的优化

https://blog.csdn.net/wodewutai17quiet/article/details/78187386

锁消除:java在编译时经过上下文扫描去除某些不可能出现共享资源竞争的锁

eg:StringBuffer的append方法是一个同步方法,若是StringBuffer类型的变量是一个局部变量,则该变量就不会被其它线程所使用,即对局部变量的操做是不会发生线程不安全的问题。在这种情景下,JVM会在JIT编译时自动将append方法上的锁去掉。

锁粗化:将多个连续的加锁和释放操做合并成一个大的锁

eg:举例:在for循环里的加锁/解锁操做,通常须要放到for循环外

无锁/偏向锁/轻量级锁/重量级锁:锁一共有4种状态,级别从低到高依次是:无锁状态、偏向锁、轻量级锁、重量级锁。锁的状态会随着竞争状况逐渐升级,而且只能够升级而不能降级。

偏向锁:

背景:大多数状况下,锁不只不存在多线程竞争,并且老是由同一线程屡次得到,为了让线程得到锁的代价更低而引入了偏向锁。

概念:核心思想就是锁会偏向第一个获取它的线程,若是在接下来的执行过程当中没有其它的线程获取该锁,则持有偏向锁的线程永远不须要同步。

目的:偏向锁其实是一种优化锁,其目的是为了减小数据在无竞争状况下的性能损耗。

原理:当一个线程访问同步块并获取锁时,会在对象头和栈帧中的锁记录里存储锁偏向的线程ID。之后该线程在进入和退出同步块时就不须要进行CAS操做来加锁和解锁,只需简单地判断一下对象头的Mark Word里是否存储着指向当前线程的偏向锁。

偏向锁的获取:

访问Mark Word中偏向锁的标识位是否为1,若是是1,则肯定为偏向锁。若是偏向锁的标识位为0,说明此时是处于无锁状态,则当前线程经过CAS操做尝试获取偏向锁,若是获取锁成功,则将Mark Word中的偏向线程ID设置为当前线程ID;而且将偏向标识位设为1。若是偏向锁的标识位不为1,也不为0(此时偏向锁的标识位没有值),说明发生了竞争,偏向锁已经膨胀为轻量级锁,这时使用CAS操做尝试得到锁。若是是偏向锁,则判断Mark Word中的偏向线程ID是否指向当前线程,若是偏向线程ID指向当前线程,则代表当前线程已经获取到了锁;若是偏向线程ID并未指向当前线程,则经过CAS操做尝试获取偏向锁,若是获取锁成功,则将Mark Word中的偏向线程ID设置为当前线程ID;若是CAS获取偏向锁失败,则表示有竞争。当到达全局安全点时(在这个时间点上没有正在执行的字节码),得到偏向锁的线程被挂起,偏向锁升级为轻量级锁,而后被阻塞在安全点的线程继续往下执行同步代码。

偏向锁的释放:

当其它的线程尝试获取偏向锁时,持有偏向锁的线程才会释放偏向锁。

释放偏向锁须要等待全局安全点(在这个时间点上没有正在执行的字节码)。过程:首先暂停拥有偏向锁的线程,而后检查持有偏向锁的线程是否活着,若是线程不处于活动状态,则将对象头设置成无锁状态,若是线程还活着,说明此时发生了竞争,则偏向锁升级为轻量级锁,而后刚刚被暂停的线程会继续往下执行同步代码。

优势:加锁和解锁不须要额外的消耗,和执行非同步方法相比仅存在纳秒级的差距

轻量级锁:(自旋)

当使用轻量级锁(锁标识位为00)时,线程在执行同步块以前,JVM会先在当前线程的栈桢中建立用于存储锁记录的空间,并将对象头中的Mark Word复制到锁记录中(注:锁记录中的标识字段称为Displaced Mark Word)。

将对象头中的MarkWord复制到栈桢中的锁记录中以后,虚拟机将尝试使用CAS将对象头中Mark Word替换为指向该线程虚拟机栈中锁记录的指针,此时若是没有线程占有锁或者没有线程竞争锁,则当前线程成功获取到锁,而后执行同步块中的代码。

若是在获取到锁的线程执行同步代码的过程当中,另外一个线程也完成了栈桢中锁记录的建立,而且已经将对象头中的MarkWord复制到了本身的锁记录中,而后尝试使用CAS将对象头中的MarkWord修改成指向本身的锁记录的指针,可是因为以前获取到锁的线程已经将对象头中的MarkWord修改过了(而且如今还在执行同步体中的代码,即仍然持有着锁),因此此时对象头中的MarkWord与当前线程锁记录中MarkWord的值不一样,致使CAS操做失败,而后该线程就会不停地循环使用CAS操做试图将对象头中的MarkWord替换为本身锁记录中MarkWord的值,(当循环次数或循环时间达到上限时中止循环)若是在循环结束以前CAS操做成功,那么该线程就能够成功获取到锁,若是循环结束以后依然获取不到锁,则锁获取失败,对象头中的MarkWord会被修改成指向重量级锁的指针,而后这个获取锁失败的线程就会被挂起,阻塞了。

当持有锁的那个线程执行完同步体以后,使用CAS操做将对象头中的MarkWord还原为最初的状态时(将对象头中指向锁记录的指针替换为Displaced Mark Word ),发现MarkWord已被修改成指向重量级锁的指针,所以CAS操做失败,该线程会释放锁并唤起阻塞等待的线程,开始新一轮夺锁之争,而此时,轻量级锁已经膨胀为重量级锁,全部竞争失败的线程都会阻塞,而不是自旋。

重量级锁:

java6以前的synchronized属于重量级锁,效率低下,由于monitor是依赖操做系统的Mutex Lock(互斥量)来实现的。

多线程竞争锁时,会引发线程的上下文切换(即在cpu分配的时间片尚未用完的状况下进行了上下文切换)。

操做系统实现线程的上下文切换须要从用户态转换到核心态,这个状态之间的转换须要相对较长的时间,时间成本相对较高。

在互斥状态下,没有获得锁的线程会被挂起阻塞,而挂起线程和恢复线程的操做都须要从用户态转入内核态中完成。

2.JVM一般能够对那些参数进行调优

https://blog.csdn.net/beyond59241/article/details/73719253

堆大小设置:年轻代大小,线程堆栈大小,年轻代中的比例,gc回收的年龄

回收器的选择:例若有吞吐量有限的回收器,也有追求最小gc停顿的回收器(CMS)

3.垃圾回收器的对比

https://blog.csdn.net/high2011/article/details/80177473

串行:垃圾回收时停掉全部用户线程,并单线程回收。为单线程环境而设计,最稳定以及效率高的收集器,可能会产生较长的停顿。

并行和parnew:Parallel收集器更关注系统的吞吐量。能够经过参数来打开自适应调节策略,虚拟机会根据当前系统的运行状况收集性能监控信息,动态调整这些参数以提供最合适的停顿时间或最大的吞吐量。

CMS(concurrent marked sweep):以获取最短回收停顿时间为目标的收集器。目前很大一部分的Java应用都集中在互联网站或B/S系统的服务端上,这类应用尤为重视服务的响应速度,但愿系统停顿时间最短,以给用户带来较好的体验。

G1:可预测停顿,这是G1的另外一大优点,下降停顿时间是G1和CMS的共同关注点,但G1除了追求低停顿外,还能创建可预测的停顿时间模型,能让使用者明确指定在一个长度为N毫秒的时间片断内,消耗在垃圾收集上的时间不得超过N毫秒,这几乎已是实时Java(RTSJ)的垃圾收集器的特征了。

选择的依据应该来源:应用程序的场景,硬件的制约,吞吐量的需求

只是控制台的单线程程序,简单任务,而且机器配置不高(串行)

( 并行 )垃圾回收器是64bit server默认的垃圾回收器

(CMS)垃圾回收器是对并行垃圾回收器的一个优化,它以CPU和系统资源为代价,换取GC的延迟.

(G1垃圾回收器)是针对于大heap的垃圾回收器,若是heap分配的足够大,分的region的优先级回收策略会优先清理垃圾多的region.而且减小了内存空间碎片

4.组合索引的应用

http://www.javashuo.com/article/p-gqpxmgkz-k.html

https://blog.csdn.net/u014590757/article/details/79590561

a.单独创建索引的效率小于组合索引,由于MYSQL只会选择一个索引进行。

b.MySQL查询只使用一个索引,所以若是where子句中已经使用了索引的话,那么order by中的列是不会使用索引的。所以数据库默认排序能够符合要求的状况下不要使用排序操做;尽可能不要包含多个列的排序,若是须要最好给这些列建立复合索引。

c.对于复合索引,在查询使用时,最好将条件顺序按找索引的顺序,这样效率最高;     select * from table1 where col1=A AND col2=B AND col3=D

d.检查是否用到了索引:能够经过执行时间来进行对比,也能够explain显示了mysql如何使用索引来处理select语句以及链接表。能够帮助选择更好的索引和写出更优化的查询语句。

e.where b=45 and a=3 and c=5 .... 这个跟第一个同样,所有发挥做用,abc只要用上了就行,跟写的顺序无关。where里面的条件顺序在查询以前会被mysql自动优化。

5.tcp若是一个包的ack丢失

tcp接收端只确认数据流中第一个丢失字节为止的字节。(累计确认)若是在超时先后续的ack到达,则代表此时已经收到。若是超时,发送方会重传并重启定时器,而后接收方会丢弃该报文。

6.tcp的两个窗口

滑动窗口:在接收方,用于告诉发送方还有多少可用的缓存空间。由接收主机经过把Rwindow值放在给A的报文段中通知剩余空间。用于流量控制。

拥塞窗口:对于数据的发送端就是拥塞窗口了,拥塞窗口不表明缓存,拥塞窗口指某一源端数据流在一个RTT内能够最多发送的数据包数,cwnd:发送端窗口, 发送端主动控制控制cwnd

发送方窗口的上限值 = Min [ rwnd, cwnd ]

当rwnd < cwnd 时,是接收方的接收能力限制发送方窗口的最大值。

当cwnd < rwnd 时,则是网络的拥塞限制发送方窗口的最大值。

https://blog.csdn.net/lishanmin11/article/details/77092652

7.Java中的注解@及应用

https://www.cnblogs.com/cainiaodongdong/p/8018182.html

http://www.javashuo.com/article/p-wcisekvw-gr.html

注解:像标签同样是对抽象事务的解释,注解就和其实同 class 和 interface 同样,注解也属于一种类型

@Documented –注解是否将包含在JavaDoc中
@Retention –何时使用该注解
@Target? –注解用于什么地方
@Inherited – 是否容许子类继承该注解
提供信息给编译器: 编译器能够利用注解来探测错误和警告信息 
编译阶段时的处理: 软件工具能够用来利用注解信息来生成代码、Html文档或者作其它相应处理。 
运行时的处理: 某些注解能够在程序运行的时候接受代码的提取

8.Java中的反射可否更改具体的代码

https://blog.csdn.net/qq_27093465/article/details/52254924

能够作到修改属性的值,可是没法修改代码。

9.如何查看某个线程最近使用了哪些文件

https://blog.csdn.net/shinyv2062/article/details/51063074

ps -ef | grep xx

获得进程号以后 ls /proc/id号/fd | wc -l 能够获得

10.tcp的包是否都是等长度的

tcp可从缓存中取出并放入报文段的数据量受限与最大报文段长,一般根据最初肯定的最大链路层帧长度来设置(MTU)