学习了Jconsole的使用,以下例子表示如何通过Jconsole监控JVM内存
import java.util.ArrayList; import java.util.List; public class TestJconsole { public byte [] b1 = new byte[128*1024]; public static void main(String[] args) { try { Thread.sleep(5000); } catch (InterruptedException e) { e.printStackTrace();// TODO: handle exception } System.out.println("start.."); fill(1000); } private static void fill(int i) { // TODO Auto-generated method stub List<TestJconsole> jlist = new ArrayList<TestJconsole>(); for (int j = 0; j < i; j++) { try { Thread.sleep(100); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } jlist.add(new TestJconsole()); System.out.println(j); } } }
如下连接运行的Java程序,转到内存页面。
如下是内存池,堆内存为线程共享区的堆内存总和。
其中可见Old Gen老年代,新生代包括(Eden Space区,Survivor区)
Metaspace为元空间,JDK8使用其取代永久区:
参见:https://www.jianshu.com/p/b99c9ef0eb16
- java8移除了permanent generation,然后class metadata存储在native memory中,其大小默认是不受限的,可以通过-XX:MaxMetaspaceSize来限制
- 如果开启了-XX:+UseCompressedOops及-XX:+UseCompressedClassesPointers(
默认是开启
),则UseCompressedOops会使用32-bit的offset来代表java object的引用,而UseCompressedClassPointers则使用32-bit的offset来代表64-bit进程中的class pointer;可以使用CompressedClassSpaceSize来设置这块的空间大小 -
如果开启了指针压缩,则CompressedClassSpace分配在MaxMetaspaceSize里头,即MaxMetaspaceSize=Compressed Class Space Size + Metaspace area (excluding the Compressed Class Space) Size
运行程序后可发现各个内存池如下:
1.因为循环过程中一直在堆中创建对象在方法体中有引用,无法回收,一直增大。
2.内存分配一直增大,从而使新生代内存区域逐渐增加,无法容纳所有的对象,所以会触发minorGC,但是还具有引用无法回收垃圾,所以触发分配担保,把新生代的内存转移到老年代,一段一段7次分配到老年代,因为每次创建的是128KB的空间,并不是大对象不会直接分配到,总共创建1000次,也就是128M左右大小,符合预期结果。
以下是GC收集次数,大于老年代对象转移次数。
3.Eden区每次容量满了触发分配担保,分到老年代,刚好也是7次
.
4.以下是survivor中,在后期新生代根据复制算法,把Eden区转移到了survivor区中。
5.总的元空间大小发生的变化缓慢。