阿里面试官这样问Java 垃圾回收,我的回答让他竖起了大拇指!
2020/3/29 17:02:49
本文主要是介绍阿里面试官这样问Java 垃圾回收,我的回答让他竖起了大拇指!,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!
前言
这周我投递出了简历,岗位是java后端开发工程师。这周阿里面试官给我进行了面试。面试过程中他问了java垃圾回收机制以及算法,今天结合面试官的三个问题详细讲一讲java的垃圾回收机制。
java对象
面试官大佬:如何判断java对象已经被回收
我:(这可难不到我)
引用计数
为每个对象存储一个计数RC,当有其他引用指向它时,计数RC++;当其他引用与其断开时,RC--;如果有RC=0,则回收它(及其所以指向的object)。
可达性分析法(根搜索算法)
把内存中的每一个对象都看作一个节点,并且定义了一些对象作为根节点“GC Roots”。**以“GC Root”的对象作为起始点,开始向下搜索,搜索所走过的路径称为引用链。**如果一个对象与起始点没有任何引用链,则说明不可用,需要被回收。
图示object6、7、8与起始点没有任何引用链,则说明不可用,需要被回收。
面试官大佬:谈一谈JVM垃圾回收算法的进化
我:(这可难不到我)
JVM(java虚拟机)的内存结构
Native Stacks | 本地方法栈 |
---|---|
PC | 代码行号指示器,用于跳转下一条需要执行的命令 |
Method area | 用于存储被VM加载的 类信息、常量、静态变量等 |
垃圾回收基本算法
标记-清除算法
定义:为每个object设定状态位(live/dead)并记录,即mark阶段;将 标记为dead的对象进行清理,即sweep阶段。
简单来说,首先标记出所有需要回收的对象,在标记完成后统一回收掉所有被标记的对象。
标记过程:
清除过程: 存在问题:-
效率不高:标记和清除过程的效率都不高
-
标记清除之后会产生大量不连续的内存碎片,空间碎片太多可能会导致大对象无法分配到足够的连续内存,从而不得不提前触发GC,甚至Stop The World
标记-整理
首先标记出所有需要回收的对象
在标记完成后让所有存活的对象都向一端移动
最后直接清理掉端边界以外的内存
优点:避免碎片化缺点:时间消耗太长,影响程序本身
复制算法
该GC策略与标记-整理的区别在于:不是在同一个区域内进行整理,而是将live对象全部复制到另一个区域。
将可用内存按照容量划分为大小相等的两块,每次只使用其中的一块。
当这一块的内存用完了。首先标记出所有需要回收的对象,在标记完成后统一回收掉所有被标记的对象,每次只使用其中的一块。
当一块的内存用完了,将还存活着的对象复制到另外一块上面,然后清理已使用过的内存空间。
flip(){ Fromspace, Tospace = Tospace, Fromspace top_of_space = Tospace + space_size scan = free = Tospace for R in Roots {R = copy(R)} while scan < free { for P in Children(scan) {*P = copy(*P)} scan = scan + size (scan) } } copy(P) { if forwarded(P){return forwarding_address(P)} else { addr = free move(P,free) free = free + size(P) forwarding_address(P) = addr return addr } } 复制代码
优点:
-
免费压缩空间
-
所有对象大小的分配都非常便宜:只需增加空闲指针即可分配
-
仅处理实时数据(通常是堆的一小部分)
-
固定的空间开销:释放和扫描指针
-
全面:自然收集的循环垃圾
-
易于实施并且合理有效
存在问题:
-
效率问题:在效率存活率较高时,复制次数多,效率低
-
空间问题:內存缩小了一半;需要额外空间做分配担保(老年代)
分代回收算法
Java堆分为新生代、老年代和永久区(Java 8之后改名为Metaspace)。
针对不同的区域,使用不同的GC策略
-
在新生代中,只有一小部分对象可较长时间 存活,选用复制算法
-
针对年老代:这里的对象有很高的幸存度,使用“标记-清理”或“标记-整理”算法。
JVM调优
面试官大佬:详细说说一次你JVM调优的经历
我:(这可难不到我)
问题
Young GC较为频繁。查看服务器的JVM参数如下-Xms1000M -Xmx1800M -Xmn350M -Xss300K -XX:+DisableExplicitGC -XX:SurvivorRatio=4 -XX:+UseParNewGC -XX:+UseConcMarkSweepGC -XX:CMSInitiatingOccupancyFraction=70 -XX:+CMSParallelRemarkEnabled 复制代码
-Xms表示初始化堆内存
-Xmx表示最大堆内存
-Xmn表示新生代内存
-XX:SurvivorRatio=4表示新生代的Eden是4/10,S1和S2各占3/10
因此Eden的内存大小为:0.435010241024字节, 为14010241024**字节
程序
/** * @date : 2020-03-22 09:48 **/ public class JavaHeapTest { public final static int OUTOFMEMORY = 500 * 1024 * 1024; private String oom; StringBuffer tempOOM = new StringBuffer(); public JavaHeapTest(int leng) { int i = 0; while (i < leng) { i++; try { tempOOM.append("a"); } catch (OutOfMemoryError e) { e.printStackTrace(); break; } } this.oom = tempOOM.toString(); } public String getOom() { return oom; } public static void main(String[] args) { for(int i=0;i<50;i++) { JavaHeapTest javaHeapTest = new JavaHeapTest(OUTOFMEMORY); System.out.println(javaHeapTest.getOom().length()); } } } 复制代码
原因
年轻代分为1个Eden和2个Survivor区(一个是from,另一个是to)。新创建的对象一般都会被分配到Eden区,如果经过第一次GC后仍然存活,就会被移到Survivor区。Survivor区中的对象每经过一次Minor GC,年龄+1,当年龄增加到一定程度时,会被移动到年老代。
OUTOFMEMORY = 500 * 1024 * 1024,大于Eden内存的大小。新生代分配内存小,导致YoungGC的频繁触发。
初始化堆内存没有和最大堆内存一致,在每次GC后进行内存可能重新分配。
解决方法
提升新生代大小
将初始化堆内存设置为最大内存
将SurvivorRatio由4修改为8,让垃圾在新生代时尽可能的多被回收掉
-Xmn350M -> -Xmn800M -XX:SurvivorRatio=4 -> -XX:SurvivorRatio=8 -Xms1000m ->-Xms1800m 复制代码
效果
YoungGC次数明显减少
总结
关于对象从出生到回收的全过程,看到一段很棒的话分享一下。
“我是一个普通的java对象,我出生在Eden区,在Eden区我还看到和我长的很像的小兄弟,我们在Eden区中玩了挺长时间。”
“有一天Eden区中的人实在是太多了,我就被迫去了Survivor区的“From”区,自从去了Survivor区,我就开始漂了,有时候在Survivor的“From”区,有时候在Survivor的“To”区,居无定所。
“直到我18岁的时候,爸爸说我成人了,该去社会上闯闯了。于是我就去了年老代那边,年老代里,人很多,并且年龄都挺大的,我在这里也认识了很多人。在年老代里,我生活了20年(每次GC加一岁),然后被回收。”
参考链接:blog.csdn.net/wuzhiwei549…
这篇关于阿里面试官这样问Java 垃圾回收,我的回答让他竖起了大拇指!的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!
- 2024-06-15matplotlib作图不显示3D图,怎么办?
- 2024-06-1503-Loki 日志监控
- 2024-06-1504-让LLM理解知识 -Prompt
- 2024-06-05做软件测试需要懂代码吗?
- 2024-06-0514-ShardingSphere的分布式主键实现
- 2024-06-03为什么以及如何要进行架构设计权衡?
- 2024-05-31全网首发第二弹!软考2024年5月《软件设计师》真题+解析+答案!(11-20题)
- 2024-05-31全网首发!软考2024年5月《软件设计师》真题+解析+答案!(21-30题)
- 2024-05-30【Java】百万数据excel导出功能如何实现
- 2024-05-30我们小公司,哪像华为一样,用得上IPD(集成产品开发)?