zoukankan      html  css  js  c++  java
  • 第十八篇 JVM之运行时数据区<14>: 方法区的垃圾收集

     《Java虚拟机规范》中提到过可以不要求虚拟机在方法区实现垃圾收集,方法区的垃圾收集性价比是比较低的,这是因为方法内容的回收判定条件相当苛刻,方法区的垃圾回收主要针对运行时常量池和类型数据。

     常量池回收判定:和Java堆中的对象相似,如果一个常量,没有虚拟机任何地方被引用,就会被回收,这里常量包括各种字面量和符号引用。

     类型回收判定:判断一个类不在被使用,需要满足下面三个条件:

    • 该类所有的实例都被回收,即Java堆中已经不存在该类及其任何派生子类的实例。
    • 加载该类的类加载器已经被回收,这个条件除非是经过精心设计的可替换类加载器的场景,如OSGi、JSP的重加载等,否则通常是很难达成的。
    • 该类对应的java.lang.Class对象没有在任何地方被引用,无法在任何地方通过反射访问该类的方法。

       虚拟机允许对满足上面三个条件的类进行回收,但仅仅是允许,不是必然就会被回收,HotSpot虚拟机提供JVM参数控制-Xnoclassgc参数进行控制,还可以使用-verbose: class以及-XX: + TraceClass-Loading、-XX:+TraceClassUnI oading查看类加载和卸载信息,其中-verbose: class和-XX:+TraceClassLoading可以在Product版的虚拟机中使用,-XX: +TraceClassUnLoading参数需要FastDebug版口的虚拟机支持。

      在大量使用反射、动态代理、CGLib等字节码框架,动态生成JSP以及OSGi这类频繁自定义类加载器的场景中,通常都需要Java虚拟机具备类型卸载的能力,以保证不会对方法区造成过大的内存压力,Spring、Mybatis都大量使用了动态代理。


    上面描述摘抄自《深入理解Java虚拟机》,前面的篇章说过方法区用于存储虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。那这些数据在类加载以后,在方法区到底是什么样的一种结构呢?

      HotSpot是基于c++实现,而c++是一门面向对象的语言,本身是具备面向对象基本特征的,所以Java中的对象表示,最简单的做法是为每个Java类生成一个c++类与之对应。但HotSpot JVM并没有这么做,而是设计了一个OOP-Klass模型,OOP(Ordinary Object Pointer)指的是普通对象指针,而Klass用来描述对象实例的具体类型。OPP就不在这里描述了,介绍一下位于方法区的Klass模型,加载到方法区的每个类都有一个与之对应的Klass类实例,这里的Klass类并不是Java语言层面的类,而是C++层面。

    Klass类部分继承关系如上:

    • InstanceKlass: 描述Java类的元信息,加载到方法区的类都会对应一个InstanceKlass实例
    • InstanceMirrorKlass: 描述java.lang.Class的实例,类加载以后会在java堆中生成一个类对应的java.lang.Class实例作为程序访问方法区类型信息的外部接口,这个实例就是InstanceMirrorKlass实例,实际是一个C++的实例,学名镜像类。
    • InstanceRefKlass:描述java/lang/ref/Reference类的子类
    • InstanceClassLoaderKlass:用于遍历某个加载器加载的类
    • ArrayKlass:描述数组类型,java中的数组类型是在运行时动态生成,Java数据的元信息由下面两个子类描述。
    • TypeArrayKlass: 描述基本类型的数组。
    • ObjArrayKlass:描述对象类型的数组

      根据上面的描述可以得出如下图的指向关系,Java对象实例的类型指针指向类元数据,表示类元数据的InstanceKlass实例中有一个java mirror(Java镜像类指针)指向java.lang.Class实例。

      通过Open JDK的instanceKlass.hpp和JHSDB工具都可以验证该指向关系,如下1图,就是通过JHSDB工具得到的Java对象实例,_mark是Mark Word,_metadata就是类型指针,2图是类元信息的实例,其中有一个java mirror就是指向java.lang.Class。java中Object的getClass()就是通过类型指针找到类元信息再找到java.lang.Class。

    有了上面的解释,关于方法区垃圾回收的三个条件就容易理解了,当三个条件全部满足后,就没有任何地方引用该类型信息,也就可以卸载该类了。

  • 相关阅读:
    聊聊Spark的分区、并行度 —— 前奏篇
    深入探讨HBASE
    分布式流平台Kafka
    GeoServer中使用SLD样式
    OpenLayer修改WFS中的要素
    leaflet加载GeoServer的WFS服务
    OL实现属性查询的功能
    OL3实现空间查询的代码示例
    WFS—GetFeature方法
    OpenLayer+Geoserver+postgis实现路径分析
  • 原文地址:https://www.cnblogs.com/zhexuejun/p/15729641.html
Copyright © 2011-2022 走看看