实习面试总结

前言

面试经验十分有帮助

腾讯-基础开发

一面

面试时间稳定一小时,并且面试官问题明显是有安排的,因此不要在一项上回答太多占用时间,面试官对你问的问题越多,你回答的越好。才能稳,如果仅是回答的好但问的不够多也可能了解不全。

大概顺序:c c++ java 操作系统 网络 数据结构
c的inline、static、栈溢出一直问到了如何绕过防护..c++的虚表、java的string实现、为什么要不可改变、内存释放、封箱拆箱、线程进程、内核中对线程进程的处理、OSI网络知识(很不熟,直接没问)、数据结构、简单算法。inline hook。

回答不好的问题:
1
osi网络相关…这个欠缺太严重了直接好好去听课看课本吧!必须好好学这个!!!!

2
为什么安卓方法数限制
dalivk字节码中invoke,中的method reference index (16 bits)方法索引只有16位,即65536个。但是检测这个错误的过程在生成dex的过程中。

3
单向链表判断环
我给出的方法是使用hash表记录遍历过的对象地址,更好的实现是使用俩个指针一快一慢,有环快的一定遇到慢的,无环快的遇不到慢的。

4
操作系统内核对线程的处理
据面试官介绍是一样的处理分配,因此俩个进程的线程id一定不会重复。

二面

回答的觉得还可以的印象不深了…回答不好的重点记下补下:
ps整理完发现这不会的有点多,心里一凉

1
Rtti-当时这部分还没看
大概总结下:

1
2
3
4
5
6
主要通过俩个操作符提供RTTI
typeid:返回指针或引用所指对象的实际类型。
dynamic_cast:将基类类型的指针或引用安全的转换为派生类型的指针或引用。
对于带虚函数的类,在运行时执行RTTI操作符,返回动态类型信息;对于其他类型,在编译时执行RTTI,返回静态类型信息。
其实现就是在类虚表中加了一个新的条目

2
java虚拟机内存管理–同样..没看过..尬的很

1
2
3
4
5
6
7
8
9
10
11
12
13
14
1. 方法区-放类
2. 堆区-存对象实例
3. 本地方法栈-native方法执行用的栈
4. 虚拟机栈-java方法运行栈
5. 程序计数器-执行位置
对象可回收判定:
引用计数收集器-不适合,因为java这种容易形成复杂引用关系的语言容易形成BC互相引用这种关系导致无法释放
可达性分析法-通过一系列GC Root的对象作为起点,从这些节点相向下搜索,走过的路径为引用链。当一个对象到GC Root没有任何引用链相连,则证明对象已死。
GC Root可为虚拟机栈中引用的对象、本地方法栈中native方法引用的对象、方法静态区属性引用的对象、方法区常量区引用的对象。
现在都使用后者,怪不得我说栈式虚拟机的好处是便于对象的管理面试官说思路正确.....因为GC Root直接从栈里扫十分方便。
垃圾回收算法:标记清楚、复制算法、标记整理算法、分代收集算法

3
c++内存溢出、多次释放的检测。现想的解决方案,有大佬推荐的工具一定要研究下他的源码:valgrind
我只回答出了hook系统调用检测堆分配的内容、动态调试某块内容的回收释放时机。但是面试官问我一秒某一堆对象有上万次的计数怎么判断泄露或多释放的点
参考:https://www.ibm.com/developerworks/cn/linux/l-cn-valgrind/index.html

1
2
3
4
5
6
7
8
9
10
Valgrind是一套Linux下,开放源代码(GPL V2)的仿真调试工具的集合。Valgrind由内核(core)以及基于内核的其他调试工具组成。内核类似于一个框架(framework),它模拟了一个CPU环境,并提供服
务给其他工具;而其他工具则类似于插件 (plug-in),利用内核提供的服务完成各种特定的内存调试任务。
这种虚拟机似的做法将程序的一切控制在手中...怪不得面试官提醒我调试器的做法..不过我把调试器也是往轻量级的监控哪里想去了..
检测cpu寄存器的数值、内存的初始化、访问权等。可以轻松的发现未初始化、读写越界、内存覆盖的问题
动态内存问题
不论是多重释放还是泄露,都可以跟踪指针来确定目标的引用链与数目,当没有指针指向时为泄露、或多个指针释放。通过最后一个操纵该区域的指针可找到问题。该工具可列出所有可能的引用点
Memcheck将内存泄露分为两种,一种是可能的内存泄露(Possibly lost),另外一种是确定的内存泄露(Definitely lost)。Possibly lost 是指仍然存在某个指针能够访问某块内存,但该指针指向的已
经不是该内存首地址。Definitely lost 是指已经不能够访问这块内存。而Definitely lost又分为两种:直接的(direct)和间接的(indirect)。直接和间接的区别就是,直接是没有任何指针指向该内
存,间接是指指向该内存的指针都位于内存泄露处。

我一直在思考怎么轻量级的解决这个问题,但是始终无法解决到底哪里引用了对象内存的问题。
看了valgrind的重量级实现倒是感慨其工程量…
实际上直接匹配申请和释放处即可。最终会剩下未释放的位置,引用错误与重复释放会直接报错。

4
Climbing Stairs
开始直接回答dfs-最暴力的方法
后来走路上突然想起这肯定有数学规律…
任意一步都为f(n)=f(n-1)+f(n-2)
因为第一步走1或2剩下的就是已有的结果了…

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
1 1
2 2
3 1+3=3
4 3+2=5
int f(int n){
if(n<=2) return n;
if first=1,second=2;
int third=0;
for(int i=3;i<=n;i++){
third = first+second;
first = second;
second = third;
}
return third;
}

动态规划与上述斐波那契算法都行….前者占空间保留结果

5
c++ map使用迭代器删除
这个我当时没有理解他的考点在哪…

1
2
3
4
5
对于关联容器(如map,set,multimap,multiset),删除当前的iterator,仅仅会使当前的iterator失效,只要在erase时,递增当前的iterator即可。这是因为map之类的容器,使用了红黑树来实现,插入,删除一个结点不会对其他结点造成影响。
因此只要删完不使用这个'指针'即可
对于序列式容器(如vector,deque,list等),删除当前的iterator会使后面所有元素的iterator都失效。这是因为vector,deque使用了连续分配的内存,删除一个元素导致后面所有的元素会向前移动一个位置。
此时最好不要在这类中删除

360-安卓安全产品

前言

面完直接被拒绝…..

经历

以下是被怼的内容
1
android基本开发….忘完了…我当时瞎答了一堆…不确定的就不要瞎说了…很容易造成坏印象
activity周期:onCreate、onStart、onResume、运行、半可见-onPause、不可见-onStop、被销毁-onDestroy

service的启动方式:
startService 启动的服务:主要用于启动一个服务执行后台任务,不进行通信。停止服务使用stopService;
bindService 启动的服务:该方法启动的服务可以进行通信。停止服务使用unbindService;

2
android-ndk开发
必须释放的引用:
jclass
jobject
各类数组jxxxArray
jstring

无需释放:
jmethodid

3
smali寄存器个数
65536个寄存器,一个方法根据dex结构来看的话最大也是2字节65536个寄存器…smali相关问题还是应该在dex格式与字节码上找答案。
之前那个方法数限制是smali字节码上的问题

4
java接口中变量是public static final的
实现接口或继承抽象类的子类必须实现接口的所有方法或抽象类的所有抽象方法。抽象类作为一个基本不变的实现被继承,接口用于补充实现。

快手-移动安全

一面

狂怼vmp的真实实现,并鄙视了现有的vmp实现
要我分析市场的实现

如何针对性的保护核心算法…

字节跳动

一面

编程没写好

二面

项目中源码的细节。有些忘了

三面

编程没写好,编程习惯问题,格式与命名