1、java内存管理-方法区(元空间的简介)

我这里使用的版本为:java version "1.8.0_31",64位的机器


首先,这里会先用一个工具查看内存的信息:jconsole相关的介绍:https://docs.oracle.com/javase/1.5.0/docs/guide/management/jconsole.html


装了jdk,并且配置了环境变量,可以直接在控制台中输入jconsole,就会弹出对应的界面。

这里我首先写一段代码,仅仅是sleep一段时间,这里就可以使用jconsole来查看这一段运行着

的代码。

package cn.yishijie.jvm;import java.util.concurrent.TimeUnit;public class JDK8Memory {

    public static void main(String[] args) throws InterruptedException{

        // 睡一个小时
        TimeUnit.HOURS.sleep(1L);
    }
}

运行起来,然后直接在控制台输入jconsole:弹出以下界面


点击箭头的哪个进程,然后选择不安全连接然后就可以进去查看这个类运行的具体情况了。

直接进入到内存的那个菜单进入:



点开A区域可以看到内存的划分情况;点B可以执行一次GC;C可以查看对应的内存的使用情况;

D可以图示的看到内存情况,鼠标移动到该区域停住可以弹出该区域的名称,点击可以选中对应的区域

并展示该区域的信息。

这里分为:堆和非堆。

堆: 
  PS Old Gen: 老年代
  PS Eden Space: 新生代eden区
  PS Survivor Space: 新生代幸存区
  
非堆:
  Meta Data: 元空间
  Code Cache: 代码缓存
  Compressed Class Space: 压缩类空间

启动的时候,增加VM参数:-XX:+PrintCommandLineFlags

然后可以看到:

-XX:InitialHeapSize=133094272 -XX:MaxHeapSize=2129508352 -XX:+PrintCommandLineFlags -XX:+UseCompressedClassPointers -XX:+UseCompressedOops -XX:-UseLargePagesIndividualAllocation -XX:+UseParallelGC

可以发现这里使用的垃圾回收器: ParallelGC ,它会和Parallel Old搭配使用。其实上面的PS就是表示这个垃圾收集器组合。



1、方法区

方法区是线程共享的内存区域,用于存储jvm加载的

类型信息,用本地内存元空间(meta space)实现。

垃圾回收会被该内存进行处理,主要是对类型的卸载和

常量池的回收。

方法区在JVM启动的时候被创建,它的大小决定了能保存

的类的数量,如果超出,会出现oom的异常。


方式区使用上述的Meta Data来实现,使用的不是jvm的内存,而是物理机器的内存,

所以它的上限是物理机器的内存。

启动的时候加入虚拟机参数:-XX:+PrintFlagsFinal,在一堆输出中找到下面元空间的初始值和最大值

MetaspaceSize = 21807104 = 20.8M

MaxMetaspaceSize = 4294901760 = 4G

当元空间的大小使用量到达初始值大小就会触发一次full gc,如果还不够的话,就会扩展大小,但是最大不能

超过MaxMetaspaceSize的大小。

这里验证下full gc的产生,回看到原来的那个图,然后选中Meta Data


可以看到,我这个程序运行起来,Meta Data基本就8M多,所以我们可以通过:

-XX:MetaspaceSize=6M  6M内存
-XX:MaxMetaspaceSize=200M  200M内存
-XX:+PrintGCDetails  打印gc详情

来设置大小,从而触发full gc


这里设置了元空间的初始大小为6M,同时也发生了元空间也发生了Full GC。这里是需要为什么要jconsole连接上才会Full Gc。

后来我试了设置成4M,发现确实可以一下就打印出来。看来应该是程序本身加载的类并没有达到6M,应该是连接jconsole的时候,还会加载类,占用了一部分空间,达到8M就触发了。


这个我也做个实验了(vm参数中加入:-verbose:class即可),确实是这样子。这里我只是截取出打印的一部分记录:

[Loaded sun.rmi.transport.SequenceEntry from D:\software\program-tool\javaSE1.8\jdk1.8\jre\lib\rt.jar]
[Loaded java.util.concurrent.locks.LockSupport from D:\software\program-tool\javaSE1.8\jdk1.8\jre\lib\rt.jar]
[Loaded sun.rmi.server.MarshalOutputStream$1 from D:\software\program-tool\javaSE1.8\jdk1.8\jre\lib\rt.jar]


当元空间经过Full gc也不够的时候,就会抛出异常。

-XX:MaxMetaspaceSize=4M 设置最大大小为4M,那么对于这个程序来说肯定是不够内存的。运行程序,打印结果如下图:


可以发现,已经抛出异常。如果不是抛出这个异常,而是

Error occurred during initialization of VM MaxMetaspaceSize is too small.

那么可能你的版本比我这个java版本要小,因为这个最小值的检查,在https://bugs.java.com/bugdatabase/view_bug.do?bug_id=8024945 里面提到的版本之后就被移除了。


一般来说,元空间垃圾回收的机会比较小,因为卸载类的信息是非常严格的,所以你可以试下在jconsole中,那个执行gc的按钮,进行gc的操作,但是你会发现,基本没有回收元空间的内存,一般会触发这里gc的原因,都是那个自定义加载器加载的那些类。比如cglib技术加载的类,自定义类加载器加载的类等。


cglib的测试代码:这里你设置下-XX:MaxMetaspaceSize=14M -verbose:class 参数,然后通过jconsole,可以看到元空间在不停增大,最后就抛出oom异常的现象!

package jdk8;import net.sf.cglib.proxy.Enhancer;import net.sf.cglib.proxy.MethodInterceptor;import java.util.concurrent.TimeUnit;public class CglibAddClass {    public static void main(String[] args) throws InterruptedException{        while (true){
            TimeUnit.MILLISECONDS.sleep(100L);
            Enhancer enhancer =new Enhancer();
            enhancer.setSuperclass(CglibAddClass.class);
            enhancer.setUseCache(false);
            enhancer.setCallback((MethodInterceptor)(obj, method, arg, proxy)->
                proxy.invokeSuper(obj, arg));
            enhancer.create();
        }
    }
}


你可能感兴趣的:(1、java内存管理-方法区(元空间的简介))