java 程序执行后 强制gc_面试题:JVM,GC垃圾回收机制

GC(垃圾收集),那收集回收的是什么呢?是内存,所以在了解垃圾回收机制之前,要对Java内存有一个了解。

后期文章会首发于本专栏,也会不定时发放福利 ,欢迎关注,点赞

JAVA高级进阶​zhuanlan.zhihu.com
b54b38601de275381e86633958130916.png

一、Java内存

5e49c3293c05aa00f11bc76dc3b1fa34.png

图解:

  • 私有内存区:伴随线程的产生而产生,一旦线程终止,私有内存区也会自动消除
  • 程序计数器:指示当前程序执行到了哪一行,执行Java方法时记录正在执行的虚拟机字节码指令地址;执行本地方法时,计数器值为null
  • 虚拟机栈:用于执行Java方法,栈帧存储局部变量表,操作数栈,动态链接,方法返回地址和一些额外的符加信息。程序执行时入栈;执行完成后栈帧出栈。
  • Java堆:Java虚拟机管理的内存中最大的一块,所有线程共享,几乎所有的对象实例和数组都在这里分配内存。GC主要就是在Java堆中进行的。 堆内存又分为:新生代(新生代又分为Eden80%,Survivor20%)和老年代(Old),并且一般新生代的空间比老年代大。
  • 方法区:只有一个方法区共享。实际也是堆,只是用于存储类,常量相关的信息,来存放程序中永远不变或唯一的内容(类信息【Class对象】,静态变量,字符串常量等)。但是已经被最新的 JVM 取消了。现在,被加载的类作为元数据加载到底层操作系统的本地内存区。

了解了Java内存,接下来就来了解一下GC原理:

二、垃圾回收机制

一)GC的主要任务:

1.分配内存

2.确保被引用对象的内存不被错误的回收

3.回收不再被引用的对象的内存空间

二)垃圾回收机制的主要解决问题

1.哪些内存需要回收?

2.什么时候回收?

3.如何回收?

针对问题1——垃圾收集器会对堆进行回收前,确定对象中哪些是“存活”,哪些是“死亡”(不可能再被任何途径使用的对象)

判断方法:

1、引用计数算法

每当一个地方引用它时,计数器+1;引用失效时,计数器-1;计数值=0——不可能再被引用。

//举例:

查看运行结果,会发现并没有因为两个对象互相引用就没有回收,因此引用计数算法很难解决对象之间相互矛盾循环引用的问题。

2、可达性分析算法:

向图,树图,把一系列“GC Roots”作为起始点,从节点向下搜索,路径称为引用链,当一个对象到GC Roots没有任何引用链相连,即不可达时,则证明此对象时不可用的。

举例:一颗树有很多丫枝,其中一个分支断了,跟树上没有任何联系,那就说明这个分支没有用了,就可以当垃圾回收去烧了。

注:在Java中可作为GCRoots的对象:

1)虚拟机栈(栈帧中的本地变量表)中引用的对象;

2)方法区中类静态属性引用的对象;

3)方法区中常量引用的对象;

4)本地方法栈中JNI引用的对象。

针对问题2——什么时候回收?

即使是被判断不可达的对象,也要再进行筛选,当对象没有覆盖finalize()方法,或者finalize方法已经被虚拟机调用过,则没有必要执行;

如果有必要执行——放置在F-Queue的队列中——Finalizer线程执行。

注意:对象可以在被GC时可以自我拯救(this),机会只有一次,因为任何一个对象的finalize() 方法都只会被系统自动调用一次。并不建议使用,应该避免。使用try_finaly或者其他方式。

针对问题3——如何回收,这就牵扯到垃圾收集算法和垃圾收集器

垃圾收集算法:

1)标记—清除算法

两个阶段:标记,清除;

不足:效率问题;空间问题(会产生大量不连续的内存碎片)

2)复制算法

将可用内存按容量分为大小相等的两块,每次都只使用其中一块;

不足:将内存缩小为了原来的一半

新生代

3)标记—整理算法

标记,清除(让存活的对象都向一端移动)

老年代

最后来讲一下垃圾回收流程。

1、新建的对象,大部分存储在Eden中

2、当Eden内存不够,就进行Minor GC释放掉不活跃对象;然后将部分活跃对象复制到Survivor中(如Survivor1),同时清空Eden区

3、当Eden区再次满了,将Survivor1中不能清空的对象存放到另一个Survivor中(如Survivor2),同时将Eden区中的不能清空的对象,复制到Survivor1,同时清空Eden区

4、重复多次(默认15次):Survivor中没有被清理的对象就会复制到老年区(Old)

5、当Old达到一定比例,则会触发Major GC释放老年代

6、当Old区满了,则触发一个一次完整的垃圾回收(Full GC)

7、如果内存还是不够,JVM会抛出内存不足,发生oom,内存泄漏。

查看运行结果,会发现并没有因为两个对象互相引用就没有回收,因此引用计数算法很难解决对象之间相互矛盾循环引用的问题。

2、可达性分析算法:

向图,树图,把一系列“GC Roots”作为起始点,从节点向下搜索,路径称为引用链,当一个对象到GC Roots没有任何引用链相连,即不可达时,则证明此对象时不可用的。

举例:一颗树有很多丫枝,其中一个分支断了,跟树上没有任何联系,那就说明这个分支没有用了,就可以当垃圾回收去烧了。

注:在Java中可作为GCRoots的对象:

1)虚拟机栈(栈帧中的本地变量表)中引用的对象;

2)方法区中类静态属性引用的对象;

3)方法区中常量引用的对象;

4)本地方法栈中JNI引用的对象。

针对问题2——什么时候回收?

即使是被判断不可达的对象,也要再进行筛选,当对象没有覆盖finalize()方法,或者finalize方法已经被虚拟机调用过,则没有必要执行;

如果有必要执行——放置在F-Queue的队列中——Finalizer线程执行。

注意:对象可以在被GC时可以自我拯救(this),机会只有一次,因为任何一个对象的finalize() 方法都只会被系统自动调用一次。并不建议使用,应该避免。使用try_finaly或者其他方式。

针对问题3——如何回收,这就牵扯到垃圾收集算法和垃圾收集器

垃圾收集算法:

1)标记—清除算法

两个阶段:标记,清除;

不足:效率问题;空间问题(会产生大量不连续的内存碎片)

2)复制算法

将可用内存按容量分为大小相等的两块,每次都只使用其中一块;

不足:将内存缩小为了原来的一半

新生代

3)标记—整理算法

标记,清除(让存活的对象都向一端移动)

老年代

最后来讲一下垃圾回收流程。

1、新建的对象,大部分存储在Eden中

2、当Eden内存不够,就进行Minor GC释放掉不活跃对象;然后将部分活跃对象复制到Survivor中(如Survivor1),同时清空Eden区

3、当Eden区再次满了,将Survivor1中不能清空的对象存放到另一个Survivor中(如Survivor2),同时将Eden区中的不能清空的对象,复制到Survivor1,同时清空Eden区

4、重复多次(默认15次):Survivor中没有被清理的对象就会复制到老年区(Old)

5、当Old达到一定比例,则会触发Major GC释放老年代

6、当Old区满了,则触发一个一次完整的垃圾回收(Full GC)

7、如果内存还是不够,JVM会抛出内存不足,发生oom,内存泄漏。

我这儿整理了比较全面的JAVA相关的面试资料,

52dad6eccbcc5f8d8bc70e0d9e3005f5.png

需要领取面试资料的同学,请加群:473984645

153b70102516789fa9a9e6f371215d4b.png
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值