现在的位置: 首页 > 综合 > 正文

JAVA内存垃圾回收机制

2019年05月10日 ⁄ 综合 ⁄ 共 4006字 ⁄ 字号 评论关闭

. 什么是内存垃圾,哪些内存符合垃圾的标准 
我们在前面讲过了,堆是一个"运行时"数据区,是通过"new"等指令建立的,Java 的堆
是由Java 的垃圾回收机制来负责处理的,堆是动态分配内存大小,垃圾收集器可以自动回
收不再使用的内存空间。
也就是说,所谓的"内存垃圾"是指在堆上开辟的内存空间在不用的时候就变成了"垃圾
"。
C++或其他程序设计语言中,必须由程序员自行声明产生和回收,否则其中的资源将消
耗,造成资源的浪费甚至死机。但手工回收内存往往是一项复杂而艰巨的工作。因为要预先
确定占用的内存空间是否应该被回收是非常困难的!如果一段程序不能回收内存空间,而且
在程序运行时系统中又没有了可以分配的内存空间时,这段程序就只能崩溃。
Java 和C++相比的优势在于,这部分"垃圾"可以被Java 虚拟机(JVM)中的一个程序
发现并自动清除掉,而不用程序员自己想着"delete"了。
Java 语言提供了一个系统级的线程,即垃圾收集器线程( Garbage Collection
Thread),来跟踪每一块分配出去的内存空间,当JVM 处于空闲循环时,自动回收每一块可
以回收的内存。
1.1 垃圾回收工作机制 
垃圾收集器线程它是一种低优先级的线程,它必须在一个Java 程序的运行过程中出现
内存空闲的时候才去进行回收处理。
垃圾收集器系统有其判断内存块是否需要回收的判断标准的。垃圾收集器完全是自动被
执行的,它不能被强制执行,即使程序员能明确地判断出某一块内存应该被回收了,也不能
强制执行垃圾回收程序进行垃圾回收。
程序员可以做的只有调用"System.gc()"来"建议"执行垃圾收集器程序,但是这个垃圾
收集程序什么时候被执行以及是否被执行了,都是不不能控制的。但是虽然垃圾收集器是低
优先级的线程,却在系统内存可用量过低时,它仍然可能会突发地执行来挽救系统。
1.2 哪些符合"垃圾"标准 
如果想了解JVM 的垃圾回收,就必须要知道JVM 垃圾回收的标准。
垃圾收集器的"垃圾"标准:对象已经不能被程序中的其他程序所引用的时候,那么这个
对象的内存空间已经没有用了。
比如当一个方法执行完毕时,在这个方法中声明的对象就超出其声明周期,这时候就可
以被当作垃圾收集了,只有当这个方法被再次被调用时才会被重新创建。
例如:
另外还可以将对象的引用变量初始化为null 值,也可以来暗示垃圾收集器来收集该对
象。
例如:
1.3 finalize()在该对象垃圾回收前调用 
垃圾收集器跟踪每一个对象,把那些不可到达的对象占有的内存空间收集起来,并且在
每次进行垃圾收集之前,垃圾收集器都会调用一下finalize()方法。Java 语言允许程序员
给任何对象添加finalize( )方法,但也不能过分依赖该方法对系统资源的回收和再利用,
因为这个方法调用后的执行结果是不可预知的。对于任何给定对象,Java 虚拟机最多只调
用一次finalize 方法。
我们用这个程序来演示一下:
……
public void function(){
OBJ obj=new OBJ();
……
}
……
……
OBJ obj=new OBJ();
Obj=null;
……
我们在命令行中键入如下命令:
这时候,我们将JVM 所许可使用的最大内存设置成"1k",当内存被占满前JVM 会首先去进行
public class finalizeTest{
public static void main( String[] args ){
finalizeTest ft=new finalizeTest();
ft.loading();
byte bs[]=new byte[1450000];
}
public void loading(){
test t=new test();
t.callme();
}
}
class test{
protected void finalize(){
System.out.println("call finalize");
}
public void callme(){
System.out.println("callme");
}
}
java -Xmx1k finalizeTest
内存回收,于是失去活动的"test"对象被回收,在回收前调用了"finalize()"。
2. JVM 垃圾回收的相关知识 
JVM 使用的是分代垃圾回收的方式,主要是因为在程序运行的时候会有如下特点:
大多数对象在创建后很快就没有对象使用它了。
大多数在一直被使用的对象很少再去引用新创建的对象。
因此就将Java 对象分为"年轻"对象和"年老"对象,JVM 将内存堆(Heap)分为两个区
域,一个是"年轻"区,另一个是"老"区,Java 将这两个区域分别称作是"新生代"和"老生代
"。
"新生代"区域中,绝大多数新创建的对象都存放在这个区域里,此区域一般来说较小而
且垃圾回收频率较高,同时因为"新生代"采用的算法和其存放的对象的特点,使该区域垃圾
回收的效率也非常高。
而"老生代"区域中存放的是在"新生代"中生存了较长时间的对象,这些对象将被转移到
"老生代"区。这个区域一般要大一些而且增长的速度相对于"新生代"要慢一些,"老生代"
垃圾回收的执行频率也会低很多。
由于JVM 在垃圾回收处理时会消耗一定的系统资源,因此有时候通过JVM 启动的时候添
加相关参数来控制"新生代"区域的大小,来调整垃圾回收处理的频率非常有用。以便于我们
更合理的利用系统资源。
"新生代"区域设置参数是"-Xmn",用这个参数可以制定"新生代"区域的大小。
我们来举一个例子说明:
我们就用系统自带的程序作为例子,在命令行上键入如下指令:
上面加入了一个新的参数"XX:+PrintGCDetails",这个参数能够打印出GC 的详细信息。屏
幕输出如下(节选):
CD C:/java/demo/jfc/SwingSet2[回车]
C:/java/demo/jfc/SwingSet2>java -jar -verbose:gc
-Xmn4m XX:+PrintGCDetails SwingSet2.jar[回车]
我们需要解释一下输出的详细内容的意思,拿第一行输出来说:
"DefNew: 3469K->84K(3712K), 0.0007778 secs"是指"新生代"的垃圾回收情况,这里
的意思是从占用3469K 内存空间变为84K 内存空间,用时0.0007778秒。
"23035K->19679K(28728K), 0.0009191 secs"是指总体GC 的回收情况,整体堆空间占
用从23035K 降低到19679K 的水平,用时0.0009191秒。
那么,这时候我们在将"新生代"的内存设为8M,并把堆的最大可控值设定为32M,再去
执行,键入如下指令:
得到的结果如下(节选):
这个结果说明:
[GC [DefNew: 3469K->84K(3712K), 0.0007778 secs]
23035K->19679K(28728K), 0.0009191 secs]
[GC [DefNew: 3284K->171K(3712K), 0.0007283 secs]
22878K->19766K(28728K), 0.0008669 secs]
[GC [DefNew: 3476K->260K(3712K), 0.0008504 secs]
23071K->19855K(28728K), 0.0009862 secs]
[GC [DefNew: 3502K->87K(3712K), 0.0009267 secs]
23096K->19682K(28728K), 0.0010610 secs]
java -jar -verbose:gc -Xmn8m -Xmx32m
XX:+PrintGCDetails SwingSet2.jar[回车]
[GC [DefNew: 6633K->6633K(7424K), 0.0000684 secs]
[Tenured: 18740K->18820K(24576K), 0.0636505 secs]
25374K->18820K(32000K), 0.0639274 secs]
[GC [DefNew: 6646K->6646K(7424K), 0.0002581 secs]
[Tenured: 18820K->18884K(24576K), 0.0651957 secs]
25467K->18884K(32000K), 0.0658804 secs]
[GC [DefNew: 6611K->6611K(7424K), 0.0000668 secs]
[Tenured: 18884K->18505K(24576K), 0.0931406 secs]
25496K->18505K(32000K), 0.0934295 secs]
"[DefNew: 6633K->6633K(7424K), 0.0000684 secs]"是指"新生代"的垃圾回收情况,
这里的意思是从占用6633K 内存空间变为6633K 内存空间,用时0. 0000684秒。
"25374K->18820K(32000K), 0.0639274 secs"是指总体GC 的回收情况,整体堆空间占用从
25374K 降低到18820K 的水平,用时0. 0639274秒。
"[Tenured: 18740K->18820K(24576K), 0.0636505 secs]"是指"老生代"GC 的回收情况,整
体堆空间占用从18740K 降低到18820K 的水平,用时0.0009012秒。
通过这些参数的调整我们可以看到在处理垃圾收集问题时,从垃圾回收的频率是时间方
面的变化,我们可以根据不同程序的不同情况予以调整。
最后有必要提一下GC 的相关参数:
-XX:+PrintGCDetails 显示GC 的详细信息
-XX:+PrintGCApplicationConcurrentTime 打印应用执行的时间
-XX:+PrintGCApplicationStoppedTime 打印应用被暂停的时间
注:":"后的"+"号表示开启此选项,如果是"-"号那么表示关闭此选项。

抱歉!评论已关闭.