02-Jvm垃圾回收
如何判断对象可以回收?
引用计数法
两个对象互相引用,会导致两个对象都没法被回收。
可达性分析
- Java 虚拟机中的垃圾回收器采用可达性分析来探索所有存活的对象
- 扫描堆中的对象,看是否能够沿着 GC Root对象 为起点的引用链找到该对象,找不到,表示可以 回收
哪些对象可以作为GC root?
- 虚拟机栈(栈帧中的本地变量表)中引用的对象。
- 方法区中类静态属性引用的对象
- 方法区中常量引用的对象
- 本地方法栈中JNI(即一般说的Native方法)引用的对象
Java中的四种引用?
实线是强引用。
强引用
new关键字创建的都是强引用。GC root引用链可以找到它就不会被回收。只有当所有的强引用链断开才会被回收。没有GC ROOT 直接或者间接引用改对象才会被回收。
上面图中只有B,C对象都不引用A1对象,它才会被回收。
软引用
内存紧张时候会触发垃圾回收,回收部分软引用所引用的对象。上图中的A2.
B对象不再引用A2时,内存不足,软引用和A2之间的会断开,A2可能被回收。
private static final int _4M = 4 * 1024 * 1024;
public static void main(String[] args) {
//list和SoftReference之间是强引用,softReference和byte数组之间是软引用
ArrayList<SoftReference<byte[]>> list = new ArrayList<>();
SoftReference<byte[]> _4MByte1 = new SoftReference<>(new byte[_4M]);
SoftReference<byte[]> _4MByte2 = new SoftReference<>(new byte[_4M]);
SoftReference<byte[]> _4MByte3 = new SoftReference<>(new byte[_4M]);
SoftReference<byte[]> _4MByte4 = new SoftReference<>(new byte[_4M]);
SoftReference<byte[]> _4MByte5 = new SoftReference<>(new byte[_4M]);
list.add(_4MByte1);
list.add(_4MByte2);
list.add(_4MByte3);
list.add(_4MByte4);
list.add(_4MByte5);
for (int i = 0 ;i<list.size();i++){
System.out.println(list.get(i).get());
}
}
null
null
null
[B@5cad8086
[B@6e0be858
//会自动删除之前的弱引用引用的对象,但是弱引用对象仍然存在但是为null
内存不足时候会回收软引用引用的对象也就是byte数组,但是不会回收软引用对象。
清理软引用对象需要用到引用队列。
//引用队列,用于移除引用为空的软引用对象
ReferenceQueue<byte[]> queue = new ReferenceQueue<>();
//list和SoftReference之间是强引用,softReference和byte数组之间是软引用
ArrayList<SoftReference<byte[]>> list = new ArrayList<>();
SoftReference<byte[]> _4MByte1 = new SoftReference<>(new byte[_4M],queue);
SoftReference<byte[]> _4MByte2 = new SoftReference<>(new byte[_4M],queue);
SoftReference<byte[]> _4MByte3 = new SoftReference<>(new byte[_4M],queue);
SoftReference<byte[]> _4MByte4 = new SoftReference<>(new byte[_4M],queue);
SoftReference<byte[]> _4MByte5 = new SoftReference<>(new byte[_4M],queue);
list.add(_4MByte1);
list.add(_4MByte2);
list.add(_4MByte3);
list.add(_4MByte4);
list.add(_4MByte5);
//遍历引用队列,如果有元素,则移除
Reference<? extends byte[]> poll = queue.poll();
while(poll != null) {
//引用队列不为空,则从集合中移除该元素
list.remove(poll);
//移动到引用队列中的下一个元素
poll = queue.poll();
}
for (int i = 0 ;i<list.size();i++){
System.out.println(list.get(i).get());
}
弱引用
只有弱引用引用该对象时,在垃圾回收时,无论内存是否充足,都会回收弱引用所引用的对象.
B对象不再引用A3,A3会直接别回收。
弱引用对象:WeakReference,使用方法和上面的软引用类似。
虚引用
虚引用的对象类垃圾回收时,虚引用自己会进入引用队列,间接的一个线程调用虚引用对象的一个方法调用Unsafe.freeMenory释放内存。NIO操作,释放直接内存。
终结器引用
Object类有一个finalize方法。当某个对象不再被其他的对象所引用时,会先将终结器引用对象放入引用队列中,然后根据终结器引用对象找到它所引用的对象,调用被引用对象的finalize方法,然后该对象可以被垃圾回收。
线程优先级较低,不一定能及时被执行到。
Jvm怎么进行垃圾回收?垃圾回收算法
标记删除
速度快,但会造成内存碎片化。
标记整理
速度慢,没有内存碎片。
复制
不会有内存碎片,需要占用双倍的内存空间。
分代垃圾回收机制
新生代
分为三块区域伊甸园,幸存区1,幸存区2
对象首先分配在伊甸园区,当伊甸园内存不足时候,触发Minor gc。第一次Minor GC时候还存活的对象会copy复制到From区年龄加一,等第二次垃圾回收的时候会扫描from区域,检查时候有可以删除的垃圾。如果第二次Minor GC还是不能删除的话年龄再累加一。当年龄达到最大寿命15(4bit存储在对象头)时,会将幸存区对象放入老年代。
- Minor GC 会引发 stop the world。暂停其他线程执行,进行垃圾回收。
老年代
当老年代内存不足时候先尝试触发Minor GC ,空间仍然不足时候触发Full GC,stw时间更长。回收整个Jvm内存,包括新生代
VM参数
描述 | 参数设置 | |
---|---|---|
堆初始大小 | -Xms | -Xms20M |
堆最大大小 | -Xmx 或 -XX:MaxHeapSize=size | -Xmx20M |
新生代大小 | -Xmn 或 (-XX:NewSize=size + -XX:MaxNewSize=size ) | -Xmn10M |
幸存区比例(动态) | -XX:InitialSurvivorRatio=ratio 和 -XX:+UseAdaptiveSizePolicy | |
幸存区比例 | -XX:SurvivorRatio=ratio | =8标识伊甸园大小为8剩下两份from,to均分->8-1-1 |
晋升阈值 | -XX:MaxTenuringThreshold=threshold | 新生代晋升老年代的阈值 |
晋升详情 | -XX:+PrintTenuringDistribution | |
GC详情 | -XX:+PrintGCDetails -verbose:gc | |
FullGC 前 MinorGC | -XX:+ScavengeBeforeFullGC |
/**
* vm参数设置:
* -Xms20M -Xmx20M -Xmn10M -XX:+UseSerialGC -XX:+PrintGCDetails -verbose:gc
* @param args
*/
public static void main(String[] args) {
}
Heap
def new generation total 9216K, used 2180K [0x00000000fec00000, 0x00000000ff600000, 0x00000000ff600000) //新生代概述:总内存9M(有1M的To区域复制使用),
eden space 8192K, 26% used [0x00000000fec00000, 0x00000000fee21378, 0x00000000ff400000)//伊甸园8m
from space 1024K, 0% used [0x00000000ff400000, 0x00000000ff400000, 0x00000000ff500000)
to space 1024K, 0% used [0x00000000ff500000, 0x00000000ff500000, 0x00000000ff600000)
tenured generation total 10240K, used 0K [0x00000000ff600000, 0x0000000100000000, 0x0000000100000000)//老年代概述:总内存10M
the space 10240K, 0% used [0x00000000ff600000, 0x00000000ff600000, 0x00000000ff600200, 0x0000000100000000)//空间10M,使用率0%
Metaspace used 3229K, capacity 4496K, committed 4864K, reserved 1056768K//元空间
class space used 349K, capacity 388K, committed 512K, reserved 1048576K
垃圾回收演示
JVM参数设置:-Xms20M -Xmx20M -Xmn10M -XX:+UseSerialGC -XX:+PrintGCDetails -verbose:gc
初始化堆大小20M,堆最大20M,新生代10M,使用UseSerialGC ,打印gc详细信息
大对象直接进老年代
新生代内存不足以存储的8M大小的对象,直接进入老年代
public static void main(String[] args) {
List<byte[]> list = new ArrayList<>();
list.add(new byte[_8M]);
}
Heap
def new generation total 9216K, used 2180K [0x00000000fec00000, 0x00000000ff600000, 0x00000000ff600000)
eden space 8192K, 26% used [0x00000000fec00000, 0x00000000fee21378, 0x00000000ff400000)
from space 1024K, 0% used [0x00000000ff400000, 0x00000000ff400000, 0x00000000ff500000)
to space 1024K, 0% used [0x00000000ff500000, 0x00000000ff500000, 0x00000000ff600000)
tenured generation total 10240K, used 8192K [0x00000000ff600000, 0x0000000100000000, 0x0000000100000000)
the space 10240K, 80% used [0x00000000ff600000, 0x00000000ffe00010, 0x00000000ffe00200, 0x0000000100000000)//老年代直接占用80%
Metaspace used 3228K, capacity 4496K, committed 4864K, reserved 1056768K
class space used 349K, capacity 388K, committed 512K, reserved 1048576K
正常的GC
public static void main(String[] args) {
List<byte[]> list = new ArrayList<>();
list.add(new byte[_7M]);
list.add(new byte[_7M]);
list.add(new byte[_1M]);
list.add(new byte[_1M]);
list.add(new byte[_512k]);
list.add(new byte[_128k]);
list.add(new byte[_128k]);
list.add(new byte[_128k]);
list.add(new byte[_128k]);
}
[GC (Allocation Failure) [DefNew: 1852K->669K(9216K), 0.0014500 secs] 1852K->669K(19456K), 0.0014839 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
[GC (Allocation Failure) [DefNew: 8328K->4K(9216K), 0.0037866 secs] 8328K->7792K(19456K), 0.0038069 secs] [Times: user=0.02 sys=0.00, real=0.00 secs]
[GC (Allocation Failure) [DefNew: 7254K->7254K(9216K), 0.0000090 secs][Tenured: 7788K->7788K(10240K), 0.0023580 secs] 15042K->14960K(19456K), [Metaspace: 3215K->3215K(1056768K)], 0.0023994 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
//进行了三次GC
Heap
def new generation total 9216K, used 8451K [0x00000000fec00000, 0x00000000ff600000, 0x00000000ff600000)
eden space 8192K, 100% used [0x00000000fec00000, 0x00000000ff400000, 0x00000000ff400000)
from space 1024K, 25% used [0x00000000ff400000, 0x00000000ff440f30, 0x00000000ff500000)
to space 1024K, 0% used [0x00000000ff500000, 0x00000000ff500000, 0x00000000ff600000)
tenured generation total 10240K, used 9836K [0x00000000ff600000, 0x0000000100000000, 0x0000000100000000)
the space 10240K, 96% used [0x00000000ff600000, 0x00000000fff9b080, 0x00000000fff9b200, 0x0000000100000000)
Metaspace used 3236K, capacity 4496K, committed 4864K, reserved 1056768K
class space used 350K, capacity 388K, committed 512K, reserved 1048576K
本作品采用 知识共享署名-相同方式共享 4.0 国际许可协议 进行许可。
评论已关闭