昨天北京沙尘暴,当时下班正好熬过了沙尘暴,于是准备回去,结果刚骑车走了没两步,就下起了雨,下雨就下雨吧,关键是泥雨,于是就成了这幅样子(一幅生无可恋的样子呀)。(笑可以笑,但在看记得点,学弟辛辛苦苦给码字的面经,原创不易哈~)
电话面。这一轮面试貌似没遇到什么大问题,问的问题也不难,没什么太大印象了…
只记得一个关于内存的。
就是那些确保存活的对象,例如
因为 gc 的目的是回收那些不用的对象,这些对象可以确保需要用到,所以肯定就不能回收。
当一个对象到 GC roots 的对象没有任何引用链相连,这个对象就是不可用的,最典型的例子就是
A a = new A();
a = null;
此时 a 开始指向的对象,已经没有 GC roots的对象指向它了,所以它应该被标记为不可达,我们始终注意的是,回收的永远都是堆上的不可用对象,当然它也有自救的机会,就是 A 这个类重写 finalize() 方法,然后在里面加上
B.b = this
,这里假设 b 是类 B 的一个静态变量,这样这个对象依旧不会被回收,因为有 GC roots 到它的引用链。
HotSpot首先需要枚举所有的GC Roots根节点,虚拟机栈的空间不大,遍历一次的时间或许可以接受,但是方法区的空间很可能就有数百兆,遍历一次需要很久。更加关键的是,当我们遍历所有GC Roots根节点时,我们需要暂停所有用户线程,因为我们需要一个此时此刻的”虚拟机快照”,找到此时此刻的可达性分析关系图。基于这种情况,HotSpot实现了一种叫做OopMap的数据结构,存储GC Roots对象,同时并不是每个指令都会对OopMap进行修改,这样OopMap很庞大,这里Hotspot引入了安全点,safePoint,**只会在Safe Point处记录GC Roots信息。**这也就是 CMS 常说的初始阶段的第一步,先把 GC roots 找到,然后去标记那些与 GC roots 相关的对象,我个人认为在 gc roots 中会标记引用了他的对象的符号引用的位置,这样就能够实现找到 GC roots 相关的对象了,这个跟类加载过程的加载过程的第三个动作一样,在堆上 去 new 一个对象作为一个类的入口,那这个对象肯定是知道方法区中类的位置的,所以就是说引用了 gc roots的对象的引用链都会有记录。
快照,就是存储在这个点上的所有vm的状态,包括内存和硬盘,当然也就包括了方法区中的 GC roots 根节点。
这里以 cms 收集器为代表。 初始标记:先去判断对象的可达性,如果不可达,标记为 dead,然后看是否有继承 finalize 方法,没有的话直接标记为需要 gc,如果继承了看是否是第一次执行,是第一次执行把其标记为 alive,否则标记为 dead。 并发标记:safepoint到达之后,一边继续标记还可以一边让用户并行; 最终标记:在让用户程序并行的过程中,还会产生 gc 的对象,所以还需要再标记一下; 并发清除:多线程清除。
所以,前者是从时间角度考虑,所以我们需要分代,因为扫小区域比扫大区域时间更短; 而后者则是从追赶用户分配内存的角度考虑,需要分代,这样扫的区域的回报率更高。
放一个链接,写的还是比较有条理的:https://allenwu.itscoder.com/java-gc
视频面。这一面前面面的还算okay,但是最后代码撕了一个多小时,生死未卜了…
主要问题还是在计算机网络方面吧。
大端和小端是指数据在内存中的存储模式,它由 CPU 决定:
为什么有大小端模式之分? 计算机中的数据是以字节(Byte)为单位存储的,每个字节都有不同的地址。现代 CPU 的位数(可以理解为一次能处理的数据的位数)都超过了 8 位(一个字节),PC机、服务器的 CPU 基本都是 64 位的,嵌入式系统或单片机系统仍然在使用 32 位和 16 位的 CPU。 对于一次能处理多个字节的CPU,必然存在着如何安排多个字节的问题,也就是大端和小端模式。以 int 类型的 0x12345678 为例,它占用 4 个字节,如果是小端模式(Little-endian),那么在内存中的分布情况为(假设从地址 0x 4000 开始存放): 内存地址 0x4000 0x4001 0x4002 0x4003 存放内容 0x78 0x56 0x34 0x12 如果是大端模式(Big-endian),那么分布情况正好相反: 内存地址 0x4000 0x4001 0x4002 0x4003 存放内容 0x12 0x34 0x56 0x78 我们的 PC 机上使用的是 X86 结构的 CPU,它是小端模式;51 单片机是大端模式;很多 ARM、DSP 也是小端模式(部分 ARM 处理器还可以由硬件来选择是大端模式还是小端模式)。
cookie是为会话存储的键值信息,不可跨域名(只能拿到当前域名下的cookie,包含父级域名),有有效期限,是在客户端的浏览器保存。 session是基于内存的缓存技术,用来保存针对每个用户的会话数据,通过session ID 来区分用户,存储于服务器端。 浏览器在第一次请求时,无cookie,然后服务器收到请求后,创建一个 session,用sessionid 标识,将其放入cookie中,然后客户端以后请求都带上cookie,服务器端收到消息后,解析里面的sessionid即可。
对于粘包问题先读出包头即包体长度n,然后再读取长度为n的包内容,这样数据包之间的边界就清楚了。对于半包问题先读出包头即包体长度n,由于此次读取的缓存池长度小于n,这时候就需要先缓存这部分的内容,等待下次read事件来时拼接起来形成完整的数据包。
字符串数据类型
MySQL数据类型 | 含义 |
---|---|
char(n) | 固定长度,申请的长度就是最终的长度,类似于静态数组,英文占一个字节,汉字占两个字节 |
varchar(n) | 可变长度,类似于可变数组—列表,英文和汉字都占两个字节,实际长度是它的值的实际长度+1 |
text | 存储可变长度的非Unicode数据,最大长度为2^31-1个字符。text列不能有默认值,存储或检索过程中,不存在大小写转换,后面如果指定长度,不会报错误,但是这个长度是不起作用的,意思就是你插入数据的时候,超过你指定的长度还是可以正常插入。 |
然后就一直在聊项目了 最后出了一道算法题,就是 LeetCode 上的求下一个排列数的变体题,我竟然…紧张了…