`
城的灯
  • 浏览: 150158 次
  • 性别: Icon_minigender_1
  • 来自: 上海
社区版块
存档分类
最新评论

JVM GC-----1

    博客分类:
  • 2012
阅读更多
java经过这么多年的发展,已经成为相当成熟的技术,特别是在内存的动态分配与内存回收技术已经相当成熟,一切看起来都是如此的美妙和谐。那我们为什么还要了解GC和内存分配呢?这个我想不用多说大家都应该明白,正是java的这种自动化,这个对大多数程序员的黑盒,导致排查各种内存溢出和内存泄露问题,甚至成为系统达到高并发的瓶颈。

当我们的程序在运行的过程中,程序计数器、虚拟机栈、本地方法栈都随线程而生,随线程而死;栈中的栈帧随着方法的进入退出有条不紊的进栈出栈,每一帧的内存大小基本在类结构确定下来就已知了,虽然栈帧分配多少内存会被JIT编译器进行一些优化,但是大体我们可以认为是编译器可知的。因此这些区域不需要过多考虑内存的回收问题,因为方法结束,内存就自动回收了,我们关注的是java的堆的垃圾回收。

我们在分析堆的垃圾回收之前,首先要弄清除哪些对象是应该被回收的,也就是说对象的生死怎么判断。目前我所知道的就两种算法来判断对象的存活,第一种是引用计数算法第二种是根搜索算法。引用计数算法,就是每个对象有一个引用计数器,每当有一个地方引用了该对象,计数器就加1;当引用失效时,计数器就减1;只有计数器为0的对象才可以被回收。这种算法实现方式很简单,效率也很高。这个算法在python和微软的COM中广泛使用,但是java却没有使用该算法,原因是它很难解决对象之间的相互引用问题。根搜索算法(GC Roots Tracing)被使用在java和C#中,这个算法就是从根节点开始向下搜索,搜索的路径就被成为引用连,当一个对象到GC Roots没有任何引用链可达时,就表明该对象是可以回收的了。在java中,可以作为GC Roots的对象有:1.虚拟机栈中引用的对象2.方法区中类静态属性引用的对象3.方法区中常量引用的对象4.本地方法栈JNI中引用的对象。但是并不是所有在根搜素算法中不可达的对象,都会被回收。一个对象真正的要被回收,至少要经历两次标记过程。如果对象在进行根搜索后发现没有与GC Roots相连接的引用链,那么它会被第一次标记并且进行一次筛选,筛选的条件是对象是否有必要执行finalize()方法。当对象没有重写finalize()方法,或者该方法已经被虚拟机调用过,虚拟机都认为没有必要执行。没有必要执行finalize方法的对象将会被放到一个队列中,这时虚拟机会在队列中进行第二次标记,如果还是没有到GC Roots可达引用链,那么它就会被回收。

我们已经讲了堆中对象是否存活的判定方法,下面我们得说说例外一个运行时数据区域:方法区或者叫做永久代。在堆中,特别是新生代,一次YGC就可以回收70%-95%的空间,而永久代的效率就低多了,很多人甚至认为方法区是没有垃圾回收。永久代回收的东西分为两种:无用的常量和无用的类。常量的回收很简单,比如一个String类型的数据“YY”,如果没有任何用到这个常量,如果这个时候发生内存回收,并且需要回收,该常量就会被回收。但是无用的类的判断就比较复杂了,条件有如下三个:1.该类的对象全部都被回收,堆中没有任何实例。2.加载该类的ClassLoader已经被回收。3.该类对应的Class对象没有被引用,没有地方通过反射来访问该类。当且仅当这三个条件满足,这个对象才可以被回收,但是不一定被回收,跟对象不一样。可以通过-Xnoclassgc进行控制,还可以通过-verbose:class和-XX:+TraceClassLoading、-XX:+TraceClassUnLoading查看类的加载和卸载信息。-verbose:class和-XX:+TraceClassLoading可以在product版的虚拟机中使用,但是-XX:TraceClassLoading参数需要fastdebug版的虚拟机支持。说了这多,其实在java的发展规划中,方法区也许会被Native Memory所代替。
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics