为了解决引用计数法的循环引用问题,Java 使用了可达性算法。html
跟踪收集器采用的为集中式的管理方式,全局记录对象之间的引用状态,执行时从一些列GC Roots的对象作为起点,从这些节点向下开始进行搜索全部的引用链,当一个对象到GC Roots 没有任何引用链时,则证实此对象是不可用的。java
图中,对象Object六、Object七、Object8虽然互相引用,但他们的GC Roots是不可到达的,因此它们将会被断定为是可回收的对象。ios
哪些对象能够做为 GC Roots 的对象:算法
XX 参数缓存
jinfo 举例,如何查看当前运行程序的配置服务器
1 2 3 4 5 6 7 8 9 10 |
public class HelloGC { public static void main(String[] args) { System.out.println("hello GC..."); try { Thread.sleep(Integer.MAX_VALUE); } catch (InterruptedException e) { e.printStackTrace(); } } } |
咱们可使用 jps -l
命令,查出进程 id网络
1 2 3 4 5 6 |
1923 org.jetbrains.jps.cmdline.Launcher 1988 sun.tools.jps.Jps 1173 org.jetbrains.kotlin.daemon.KotlinCompileDaemon 32077 com.intellij.idea.Main 1933 com.cuzz.jvm.HelloGC 32382 org.jetbrains.idea.maven.server.RemoteMavenServer |
在使用 jinfo -flag PrintGCDetails 1933
命令查看并发
1 |
-XX:-PrintGCDetails |
能够看出默认是不打印 GC 收集细节jvm
也但是使用jinfo -flags 1933
查看因此的参数maven
两个经典参数:-Xms 和 - Xmx(如 -Xms1024m)
查看初始默认值:-XX:+PrintFlagsInitial
1 2 3 4 5 6 7 8 9 10 11 |
cuzz@cuzz-pc:~/Project/demo$ java -XX:+PrintFlagsInitial [Global flags] intx ActiveProcessorCount = -1 {product} uintx AdaptiveSizeDecrementScaleFactor = 4 {product} uintx AdaptiveSizeMajorGCDecayTimeScale = 10 {product} uintx AdaptiveSizePausePolicy = 0 {product} uintx AdaptiveSizePolicyCollectionCostMargin = 50 {product} uintx AdaptiveSizePolicyInitializingSteps = 20 {product} uintx AdaptiveSizePolicyOutputInterval = 0 {product} uintx AdaptiveSizePolicyWeight = 10 {product} ... |
查看修改更新:-XX:+PrintFlagsFinal
1 2 3 4 5 6 7 |
bool UsePSAdaptiveSurvivorSizePolicy = true {product} bool UseParNewGC = false {product} bool UseParallelGC := true {product} bool UseParallelOldGC = true {product} bool UsePerfData = true {product} bool UsePopCountInstruction = true {product} bool UseRDPCForConstantTableBase = false {C2 product} |
= 与 := 的区别是,一个是默认,一个是人物改变或者 jvm 加载时改变的参数
打印命令行参数(能够看默认垃圾回收器):-XX:+PrintCommandLineFlags
1 2 |
cuzz@cuzz-pc:~/Project/demo$ java -XX:+PrintCommandLineFlags -XX:InitialHeapSize=128789376 -XX:MaxHeapSize=2060630016 -XX:+PrintCommandLineFlags -XX:+UseCompressedClassPointers -XX:+UseCompressedOops -XX:+UseParallelGC |
-XX:+PrintGCDetails
输出详细 GC 收集日志信息
代码
1 2 3 4 5 |
public class HelloGC { public static void main(String[] args) { byte[] bytes = new byte[20 * 1024 * 1024]; } } |
打印结果
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
[GC (Allocation Failure) [PSYoungGen: 1231K->448K(2560K)] 1231K->456K(9728K), 0.0015616 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] [GC (Allocation Failure) [PSYoungGen: 448K->384K(2560K)] 456K->392K(9728K), 0.0016999 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] [Full GC (Allocation Failure) [PSYoungGen: 384K->0K(2560K)] [ParOldGen: 8K->358K(7168K)] 392K->358K(9728K), [Metaspace: 3028K->3028K(1056768K)], 0.0066696 secs] [Times: user=0.01 sys=0.00, real=0.01 secs] [GC (Allocation Failure) [PSYoungGen: 0K->0K(2560K)] 358K->358K(9728K), 0.0005321 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] [Full GC (Allocation Failure) [PSYoungGen: 0K->0K(2560K)] [ParOldGen: 358K->340K(7168K)] 358K->340K(9728K), [Metaspace: 3028K->3028K(1056768K)], 0.0051543 secs] [Times: user=0.01 sys=0.00, real=0.01 secs] Heap PSYoungGen total 2560K, used 81K [0x00000000ffd00000, 0x0000000100000000, 0x0000000100000000) eden space 2048K, 3% used [0x00000000ffd00000,0x00000000ffd14668,0x00000000fff00000) from space 512K, 0% used [0x00000000fff00000,0x00000000fff00000,0x00000000fff80000) to space 512K, 0% used [0x00000000fff80000,0x00000000fff80000,0x0000000100000000) ParOldGen total 7168K, used 340K [0x00000000ff600000, 0x00000000ffd00000, 0x00000000ffd00000) object space 7168K, 4% used [0x00000000ff600000,0x00000000ff655188,0x00000000ffd00000) Metaspace used 3060K, capacity 4496K, committed 4864K, reserved 1056768K class space used 336K, capacity 388K, committed 512K, reserved 1048576K Exception in thread "main" java.lang.OutOfMemoryError: Java heap space at com.cuzz.jvm.HelloGC.main(HelloGC.java:12) |
-XX:MaxTenuringThreshold
在Java语言中,除了基本数据类型外,其余的都是指向各种对象的对象引用;Java中根据其生命周期的长短,将引用分为4类。
强引用
软引用
代码验证
我设置 JVM 参数为 -Xms10m -Xmx10m -XX:+PrintGCDetails
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
public class SoftReferenceDemo { public static void main(String[] args) { Object obj = new Object(); SoftReference<Object> softReference = new SoftReference<>(obj); obj = null; try { // 分配 20 M byte[] bytes = new byte[20 * 1024 * 1024]; } catch (Exception e) { e.printStackTrace(); } finally { System.out.println("软引用:" + softReference.get()); } } } |
输出
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
[GC (Allocation Failure) [PSYoungGen: 1234K->448K(2560K)] 1234K->456K(9728K), 0.0016748 secs] [Times: user=0.00 sys=0.00, real=0.01 secs] [GC (Allocation Failure) [PSYoungGen: 448K->384K(2560K)] 456K->392K(9728K), 0.0018398 secs] [Times: user=0.01 sys=0.00, real=0.00 secs] [Full GC (Allocation Failure) [PSYoungGen: 384K->0K(2560K)] [ParOldGen: 8K->358K(7168K)] 392K->358K(9728K), [Metaspace: 3030K->3030K(1056768K)], 0.0057246 secs] [Times: user=0.01 sys=0.00, real=0.01 secs] [GC (Allocation Failure) [PSYoungGen: 0K->0K(2560K)] 358K->358K(9728K), 0.0006038 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] [Full GC (Allocation Failure) [PSYoungGen: 0K->0K(2560K)] [ParOldGen: 358K->340K(7168K)] 358K->340K(9728K), [Metaspace: 3030K->3030K(1056768K)], 0.0115080 secs] [Times: user=0.01 sys=0.00, real=0.01 secs] 软引用:null Exception in thread "main" java.lang.OutOfMemoryError: Java heap space at com.cuzz.jvm.SoftReferenceDemo.main(SoftReferenceDemo.java:21) Heap PSYoungGen total 2560K, used 98K [0x00000000ffd00000, 0x0000000100000000, 0x0000000100000000) eden space 2048K, 4% used [0x00000000ffd00000,0x00000000ffd18978,0x00000000fff00000) from space 512K, 0% used [0x00000000fff00000,0x00000000fff00000,0x00000000fff80000) to space 512K, 0% used [0x00000000fff80000,0x00000000fff80000,0x0000000100000000) ParOldGen total 7168K, used 340K [0x00000000ff600000, 0x00000000ffd00000, 0x00000000ffd00000) object space 7168K, 4% used [0x00000000ff600000,0x00000000ff6552f8,0x00000000ffd00000) Metaspace used 3067K, capacity 4496K, committed 4864K, reserved 1056768K class space used 336K, capacity 388K, committed 512K, reserved 1048576K |
发现当内存不够的时候就会被回收。
弱引用
代码验证
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
public class WeakReferenceDemo { public static void main(String[] args) { Object obj = new Object(); WeakReference<Object> weakReference = new WeakReference<>(obj); System.out.println(obj); System.out.println(weakReference.get()); obj = null; System.gc(); System.out.println("GC以后...."); System.out.println(obj); System.out.println(weakReference.get()); } } |
输出
1 2 3 4 5 |
java.lang.Object@1540e19d java.lang.Object@1540e19d GC以后.... null null |
值得注意的是String name = "cuzz"
这种会放入永久代,以及 Integer age = 1
在 int 中 -128 到 127 会被缓存,因此是强引用,而后 GC 也不会被回收。
引用队列
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
public class ReferenceQueueDemo { public static void main(String[] args) throws InterruptedException { Object obj = new Object(); ReferenceQueue<Object> referenceQueue = new ReferenceQueue<>(); WeakReference<Object> weakReference = new WeakReference<>(obj, referenceQueue); System.out.println(obj); System.out.println(weakReference.get()); System.out.println(weakReference); obj = null; System.gc(); Thread.sleep(500); System.out.println("GC以后...."); System.out.println(obj); System.out.println(weakReference.get()); System.out.println(weakReference); } } |
输出
1 2 3 4 5 6 7 |
java.lang.Object@1540e19d java.lang.Object@1540e19d java.lang.ref.WeakReference@677327b6 GC以后.... null null java.lang.ref.WeakReference@677327b6 |
会把该对象的包装类即weakReference
放入到ReferenceQueue
里面,咱们能够从queue中获取到相应的对象信息,同时进行额外的处理。好比反向操做,数据清理等。
java.lang.StackOverflowError
java.lang.OutOfMemoryError : Java heap space
java.lang.OutOfMemoryError : GC overhead limit exceeded
java.lang.OutOfMemoryError : Direct buffer memory
配置参数:-Xms10m -Xmx10m -XX:+PrintGCDetails -XX:MaxDirectMemorySize=5m
1 2 3 4 5 6 |
public class DirectBufferDemo { public static void main(String[] args) { System.out.println("maxDirectMemory : " + sun.misc.VM.maxDirectMemory() / (1024 * 1024) + "MB"); ByteBuffer byteBuffer = ByteBuffer.allocateDirect(6 * 1024 * 1024); } } |
输出
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
maxDirectMemory : 5MB [GC (System.gc()) [PSYoungGen: 1315K->464K(2560K)] 1315K->472K(9728K), 0.0008907 secs] [Times: user=0.00 sys=0.00, real=0.01 secs] [Full GC (System.gc()) [PSYoungGen: 464K->0K(2560K)] [ParOldGen: 8K->359K(7168K)] 472K->359K(9728K), [Metaspace: 3037K->3037K(1056768K)], 0.0060466 secs] [Times: user=0.01 sys=0.00, real=0.00 secs] Exception in thread "main" java.lang.OutOfMemoryError: Direct buffer memory at java.nio.Bits.reserveMemory(Bits.java:694) at java.nio.DirectByteBuffer.<init>(DirectByteBuffer.java:123) at java.nio.ByteBuffer.allocateDirect(ByteBuffer.java:311) at com.cuzz.jvm.DirectBufferDemo.main(DirectBufferDemo.java:17) Heap PSYoungGen total 2560K, used 56K [0x00000000ffd00000, 0x0000000100000000, 0x0000000100000000) eden space 2048K, 2% used [0x00000000ffd00000,0x00000000ffd0e170,0x00000000fff00000) from space 512K, 0% used [0x00000000fff00000,0x00000000fff00000,0x00000000fff80000) to space 512K, 0% used [0x00000000fff80000,0x00000000fff80000,0x0000000100000000) ParOldGen total 7168K, used 359K [0x00000000ff600000, 0x00000000ffd00000, 0x00000000ffd00000) object space 7168K, 5% used [0x00000000ff600000,0x00000000ff659e28,0x00000000ffd00000) Metaspace used 3068K, capacity 4496K, committed 4864K, reserved 1056768K class space used 336K, capacity 388K, committed 512K, reserved 1048576K |
java.lang.OutOfMemoryError : unable to create new native thread
java.lang.OutOfMemoryError : Metaspace
具体的实现能够看看这个帖子:几种手动OOM的方式
老年代
垃圾收集器配置代码总结
底层原理
Region 区域化垃圾收集器:最大好处是化整为零,避免全内存扫描,只须要按照区域来进行扫描便可。
G1的内存结构和传统的内存空间划分有比较的不一样。G1将内存划分红了多个大小相等的Region(默认是512K),Region逻辑上连续,物理内存地址不连续。同时每一个Region被标记成E、S、O、H,分别表示Eden、Survivor、Old、Humongous。其中E、S属于年轻代,O与H属于老年代。
H表示Humongous。从字面上就能够理解表示大的对象(下面简称H对象)。当分配的对象大于等于Region大小的一半的时候就会被认为是巨型对象。H对象默认分配在老年代,能够防止GC的时候大对象的内存拷贝。经过若是发现堆内存容不下H对象的时候,会触发一次GC操做。
下一篇重点介绍。