JVM垃圾回收机制

什么是垃圾回收?

  垃圾回收(Garbage Collection,GC),顾名思义就是释放垃圾占用的空间,防止内存泄露。有效的使用可以使用的内存,对内存堆中已经死亡的或者长时间没有使用的对象进行清除和回收。

1.垃圾回收是否涉及栈内存?

因为栈帧对应的方法执行完后,栈会将该方法对应的栈帧弹出栈,释放内存,因此垃圾回收不涉及栈内存.

2.栈内存分配越大越好吗?

栈内存的大小不会影响方法执行的速度,而且由于计算机硬件的储存大小是有限的,栈内存分配越大,会导致最大线程数减少,得不偿失.

3.方法内的局部变量是否线程安全?.

如果方法内局部变量没有逃离方法的作用范围,它是线程安全的,因为在线程调用方法时,虚拟机会在线程的栈中开辟一个新的栈帧,而这个栈帧就对应着调用方法,因此每个方法在被调用时,都会被储存在自己所对应的线程栈中,为每个线程的私有栈帧,不会互相影响,所以局部变量是线程安全的,但共享变量如static int a=10;a变量可以被其他方法调用,每个线程都可以访问a变量,若在不同线程的方法中调用了a变量,则会引起a变量的变化,导致线程不安全.如果是局部变量引用了对象,并逃离方法的作用范围,需要考虑线程安全

4、栈帧过多导致栈内存溢出

比如递归调用没有结束语句,一直递归调用方法,一直创建新的栈帧,导致栈帧过多,出现栈内存溢出

5、栈帧过大导致栈内存溢出

单个栈帧的所需要的内存超出了栈内存大小

6、堆内存诊断(只是介绍下检测工具):

1、jps工具,可以查看当前系统中有哪些java进程

2.jmap工具,可以查看堆内存占用情况jmap - heap进程id

3、jconsole工具,图形界面的,多功能的监测工具,可以连续监测

7、内存的回收

 

Direct memory:直接内存

直接内存的回收:就是ByteBuffer对象被回收,触发了UnSafe类的回收直接内存的方法(底层用到了线程),使用了Unsafe对象完成直接内存的分配回收,并且回收需要主动调用freeMemory方法, ByteBuffer的实现类内部,使用了Cleaner(虚引用)来监测ByteBuffer对象,一旦ByteBuffer对象被垃圾回收,那么就会由ReferenceHandler线程通过Cleaner的clean方法调用freeMemory来释放直接内存

8、垃圾回收

1、如何判断内存是否可以回收?

1.1、引用计数法: 通过在对象头中分配一个空间来保存该对象被引用的次数。如果该对象被其它对象引用,则它的引用计数加一,如果删除对该对象的引用,那么它的引用计数就减一,当该对象的引用计数为0时,那么该对象就会被回收。但如果两个对象互相引用(比如类A的无参构造器中有new B(),类B的无参构造器中有new A()),就会导致这两个对象的引用计数在程序运行期间永远不会为0,导致永久无法回收这两个对象的内存.

,

当运行main方法后,直接抛出栈内存溢出异常:

2、可达性分析算法

Java 虚拟机中的垃圾回收器采用可达性分析来探索所有存活的对象扫描堆中的对象,看是否能够沿着GC Root对象为起点的引用链找到该对象,找不到,表示可以回收,而哪些对象可以作为GC Root ?

  1. 虚拟机栈中引用的对象,程序在虚拟机栈中执行,在栈中包括局部变量表和操作数栈,局部变量表中的变量可能为引用类型,它们引用的对象即可作为GC root.不过随着函数调用结束出栈,这些引用便会消失.
  2. 方法区中类静态属性引用的对象,比如 private static Object s1;
  3. 方法区中常量引用的对象,比如 private final Object s1;
  4. 本地方法栈中引用的对象,就是程序中native本地方法引用的对象

3、四种引用:

强引用:当A1对象被GCroot直接或间接引用时,都不能被回收

软引用:当A2对象被GCroot直接或间接软引用,但没有被强引用时,但当GC垃圾回收器回收后,内存仍不足,就有可能会将A2对象回收.

弱引用:当A3对象被GCroot直接或间接弱引用,但没有被强引用时,但当GC垃圾回收器回收后,无论内存够不够,都有可能会被回收.

(软弱:因为引用本身也是一个对象,当软引用或弱引用所引用的对象被回收后,这俩引用如果配合了引用队列使用,则会被放进引用队列中,如果需要释放引用本身的内存,则可以根据引用队列遍历释放,可以配合引用队列使用,也可以不配合,而虚引用和终结器引用则必须配合引用队列使用)

虚引用:当虚引用引用的对象被GC回收后,虚引用会进入到引用队列中,会有一个线程定时查找引用队列中是否新入了一个Cleaner虚引用,如果有,则根据虚引用的储存的直接内存地址来调用Unsafe.freeMemory来释放直接内存.

终结器引用:当GC垃圾回收器开始回收时,终结器引用会进入引用队列,有一个优先级很低的线程会在某时刻查看队列中是否有终结器引用,如果有,则调用A4对象的finallize()方法,在下一次回收时释放A4对象的内存,该方式效率很低,不推荐使用.

2、垃圾回收算法

  2.1、标记清除

在标记后,回收器会将未被GC Root直接或间接引用的对象所占有的内存释放,但不会直接清零,而是将被释放的内存的地址储存在空闲列表中,当需要产生新对象时,就会在空闲列表中查看这些内存能否装得下新对象,如果可以,则将新对象储存在这个内存空间中,并在栈中储存堆中该内存的地址.

优点:清除速度快,因为只需要将这些内存的起始和结束地址储存在空闲队列即可,并清除引用这些内存的对象.

缺点:会产生内存碎片,如果空闲队列中的所有内存都无法满足新对象所需要的内存,则会导致内存溢出.

2.2标记整理:

   在标记后,回收器会将未被GC Root直接或间接引用的对象所占有的内存释放,并且清除这些内存碎片,将内存进行整理,将后面的对象内存地址前移,让空间保持紧凑,保证新对象分配内存.

优点:不会产生内存碎片

缺点:效率低了,因为整理过程中会移动内存起始和结束地址,会改变对象的引用地址.

2.3复制回收:

整理时,将存活内存复制到TO中,紧凑存储,整理后,清除FROM中的失活内存,并将存活内存移到FROM中,保证TO一直是空闲内存.

优点:不会产生内存碎片

缺点:会占用双倍内存

3、分代回收:将堆内存分为新生代和老年代

长时间存活的对象放在老年代中

短时间存活的对象放在新生代中

  当第一次新对象尝试进入伊甸园,而伊甸园内存不足时,会触发一次Minor GC,将伊甸园中的内存进行一次回收复制,将存活内存放入幸存区To,清除失活内存,并将From与To进行互换,将幸存区中的存货内存幸存次数+1,第二次伊甸园内存不足时,会触发一次Minor GC,将伊甸园和幸存区From中的存活内存都进行一次回收复制,将伊甸园和幸存区From中的存活内存放入幸存区To,清除失活内存,并将From与To进行互换,将幸存区From中的存活内存幸存次数+1,(注意:经历了一次或多次幸存的内存的幸存次数只会累加,不会清零),然后重复下去,幸存区From中的存活内存的幸存次数达到某一默认值(阙值,默认最大15),就会将其移动到老年代中,直到伊甸园、幸存区From、老年代中的内存都不足以装下新对象内存时,就会触发一次Full GC,来清除所有未被GC Root直接或间接引用的内存.注:当新生代内存极为紧张时,有可能From中的存活内存的幸存次数没有达到阙值,也可能会晋升到老年代中.

相关VM参数,可以了解下.

一个线程的内存溢出不会影响所有线程

4、垃圾回收器

1、串行:

单线程、堆内存较小,适合个人电脑.

老年代采用标记整理回收算法,年轻代采用回收复制算法

2、吞吐量优先:

基于多线程、堆内存较大,需要多核cpu,让单位时间内,STW(stop the world,暂停除垃圾回收线程外的所有用户线程)的时间最短,目的是垃圾回收总时间最低

3、响应时间优先:

基于多线程、堆内存较大,需要多核cpu,尽可能让单次STW的时间最短

最合适

当前活跃对象:快要回收的对象

需要晋升对象:From区中存活对象

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值