JVM内存管理及垃圾回收详解(2)

方法区是所有线程共享的内存区域,用于存储已经被JVM加载的类信息、常量、静态变量等数据,一般来说,方法区属于持久代(关于持久代,会在GC部分详细介绍,除了持久代,还有新生代和旧生代),也难怪Java规范将方法区描述为堆的一个逻辑部分,但是它不是堆。方法区的垃圾回收比较棘手,就算是Sun的HotSpot VM在这方面也没有做得多么完美。此处引入方法区中一个重要的概念:运行时常量池。主要用于存放在编译过程中产生的字面量(字面量简单理解就是常量)和引用。一般情况,常量的内存分配在编译期间就能确定,但不一定全是,有一些可能就是运行时也可将常量放入常量池中,如String类中有个Native方法intern()<关于intern()的详细说明,请看另一篇文章:>

此处补充一个在JVM内存管理之外的一个内存区:直接内存。在JDK1.4中新加入类NIO类,引入了一种基于通道与缓冲区的I/O方式,它可以使用Native函数库直接分配堆外内存,即我们所说的直接内存,这样在某些场景中会提高程序的性能。

二、垃圾回收

有句话说的好:Java和C++之间有一堵有内存分配和垃圾回收技术围成的墙,墙外的人想进去,墙里的人想出去!这句话的意思,请读者自己去琢磨。总的来说,C、C++程序员有时苦于内存泄露,内存管理是件令人头痛的事儿,但是Java程序员呢,又羡慕C++程序员,自己可以控制一切,这样就不会在内存管理方面显得束手无策,的却如此,作为Java程序员我们很难去控制JVM的内存回收,只能根据它的原理去适应,尽量提高程序的性能。下面开始讲解Java垃圾回收,即Garbage Collection,GC。从以下四个方面进行:

1、为什么要进行垃圾回收?

随着程序的运行,内存中存在的实例对象、变量等信息占据的内存越来越多,如果不及时进行垃圾回收,必然会带来程序性能的下降,甚至会因为可用内存不足造成一些不必要的系统异常。

2、哪些“垃圾”需要回收?

在我们上面介绍的五大区中,有三个是不需要进行垃圾回收的:程序计数器、JVM栈、本地方法栈。因为它们的生命周期是和线程同步的,随着线程的销毁,它们占用的内存会自动释放,所以只有方法区和堆需要进行GC。具体到哪些对象的话,简单概况一句话:如果某个对象已经不存在任何引用,那么它可以被回收。通俗解释一下就是说,如果一个对象,已经没有什么作用了,就可以被当废弃物被回收了。

3、什么时候进行垃圾回收?

根据一个经典的引用计数算法,每个对象添加一个引用计数器,每被引用一次,计数器加1,失去引用,计数器减1,当计数器在一段时间内保持为0时,该对象就认为是可以被回收得了。但是,这个算法有明显的缺陷:当两个对象相互引用,但是二者已经没有作用时,按照常规,应该对其进行垃圾回收,但是其相互引用,又不符合垃圾回收的条件,因此无法完美处理这块内存清理,因此Sun的JVM并没有采用引用计数算法来进行垃圾回收。而是采用一个叫:根搜索算法,如下图:

JVM内存管理及垃圾回收详解

基本思想就是:从一个叫GC Roots的对象开始,向下搜索,如果一个对象不能到达GC Roots对象的时候,说明它已经不再被引用,即可被进行垃圾回收(此处 暂且这样理解,其实事实还有一些不同,当一个对象不再被引用时,并没有完全“死亡”,如果类重写了finalize()方法,且没有被系统调用过,那么系统会调用一次finalize()方法,以完成最后的工作,在这期间,如果可以将对象重新与任何一个和GC Roots有引用的对象相关联,则该对象可以“重生”,如果不可以,那么就说明彻底可以被回收了),如上图中的Object5、Object6、Object7,虽然它们3个依然可能相互引用,但是总体来说,它们已经没有作用了,这样就解决了引用计数算法无法解决的问题。

补充引用的概念JDK 1.2之后,对引用进行了扩充,引入了强、软、若、虚四种引用,被标记为这四种引用的对象,在GC时分别有不同的意义:

a> 强引用(Strong Reference).就是为刚被new出来的对象所加的引用,它的特点就是,永远不会被回收。

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/a3fb10e63f17bf6399f40a8040d69c29.html