System.gc
Java中的内存分配是随着new一个新的对象来实现的,这个很简单,而且也还是有一些可以“改进”内存回收的机制的,其中最显眼的就是这个System.gc()函数。
乍一看这个函数似乎是可以进行垃圾回收的,可事实并不是那么简单。
其实这个gc()函数的作用只是提醒虚拟机:程序员希望进行一次垃圾回收。但是它不能保证垃圾回收一定会进行,而且具体什么时候进行是取决于具体的虚拟机的,不同的虚拟机有不同的对策。
java提供了从语言角度能够强制jvm进行垃圾回收,在我们的程序中可以通过调用System.gc去强制jvm进行垃圾回收,通过源码我们可以看到实际上是调用了Runtime去强制gc
public static void gc() {
Runtime.getRuntime().gc();
}
Runtime的gc方法是native方法也就是Rumtime.c中的
JNIEXPORT void JNICALL
Java_java_lang_Runtime_gc(JNIEnv *env, jobject this)
{
JVM_GC();
}
而JVM_GC方法是在jvm.cpp中实现
JVM_ENTRY_NO_ENV(void, JVM_GC(void))
JVMWrapper("JVM_GC");
if (!DisableExplicitGC) {
Universe::heap()->collect(GCCause::_java_lang_system_gc);
}
JVM_END
我们看到参数DisableExplicitGC,从代码中可以使用-XX:+DisableExplicitGC 可以关闭system,gc
对universe:heap() -> Universe::_collectedHeap 是在初始化的时候决定使用什么类型的heap,也决定了使用什么gc的策略,也就是说java heap的分配方式是在java启动的时候就决定的,无法中间更改,同样对应的gc策略也无法更改
if (UseParallelGC) {
#ifndef SERIALGC
Universe::_collectedHeap = new ParallelScavengeHeap();
#else // SERIALGC
fatal("UseParallelGC not supported in java kernel vm.");
#endif // SERIALGC
} else if (UseG1GC) {
#ifndef SERIALGC
G1CollectorPolicy* g1p = new G1CollectorPolicy_BestRegionsFirst();
G1CollectedHeap* g1h = new G1CollectedHeap(g1p);
Universe::_collectedHeap = g1h;
#else // SERIALGC
fatal("UseG1GC not supported in java kernel vm.");
#endif // SERIALGC
} else {
GenCollectorPolicy *gc_policy;
if (UseSerialGC) {
gc_policy = new MarkSweepPolicy();
} else if (UseConcMarkSweepGC) {
#ifndef SERIALGC
if (UseAdaptiveSizePolicy) {
gc_policy = new ASConcurrentMarkSweepPolicy();
} else {
gc_policy = new ConcurrentMarkSweepPolicy();
}
#else // SERIALGC
fatal("UseConcMarkSweepGC not supported in java kernel vm.");
#endif // SERIALGC
} else { // default old generation
gc_policy = new MarkSweepPolicy();
}
Universe::_collectedHeap = new GenCollectedHeap(gc_policy);
}
从源码中我们可以看到
Parallel GC 是用的是ParallelScavengeHeap,
CMS 使用的是 GenCollectdHeap
G1使用的是G1CollectedHeap
http://blog.csdn.net/raintungli/article/details/11214157
finalize()是由JVM自动调用的,你可以用System.gc(),但JVM不一定会立刻执行,JVM感觉内存空间有限时,才会开始执行finalize(),至于新的对象创建个数和被收集个数不同是因为收集的对象只和JVM的垃圾收集策略有关。
1.构造函数
要点:
构建器(Constructor)属于一种较特殊的方法类型,因为它没有返回值.这与 void返回值存在着明显的区别。对于void返回值,尽管方法本身不会自动返回什么,但仍然可以让它返回另一些东西。构建器则不同,它不仅什么也不会自动返回,而且根本不能有任何选择.若创建一个没有构件器的类,则编译器会自动创建一个默认构件器.
2.finalize()和gc()
(1)问题:finalize()函数是干嘛的?Java不是有Garbage Collection(以下简称gc)来负责回收内存吗?
回答:
gc 只能清除在堆上分配的内存(纯java语言的所有对象都在堆上使用new分配内存),而不能清除栈上分配的内存(当使用JNI技术时,可能会在栈上分配内存,例如java调用c程序,而该c程序使用malloc分配内存时).因此,如果某些对象被分配了栈上的内存区域,那gc就管不着了,对这样的对象进行内存回收就要靠finalize().
举个例子来说,当java 调用非java方法时(这种方法可能是c或是c++的),在非java代码内部也许调用了c的malloc()函数来分配内存,而且除非调用那个了 free() 否则不会释放内存(因为free()是c的函数),这个时候要进行释放内存的工作,gc是不起作用的,因而需要在finalize()内部的一个固有方法调用它(free()).
finalize的工作原理应该是这样的:一旦垃圾收集器准备好释放对象占用的存储空间,它首先调用finalize(),而且只有在下一次垃圾收集过程中,才会真正回收对象的内存.所以如果使用finalize(),就可以在垃圾收集期间进行一些重要的清除或清扫工作.
(2)问题:finalize()在什么时候被调用?
回答:
有三种情况
1.所有对象被Garbage Collection时自动调用,比如运行System.gc()的时候.
2.程序退出时为每个对象调用一次finalize方法。
3.显式的调用finalize方法
除此以外,正常情况下,当某个对象被系统收集为无用信息的时候,finalize()将被自动调用,但是jvm不保证finalize()一定被调用,也就是说,finalize()的调用是不确定的,这也就是为什么sun不提倡使用finalize()的原因. 简单来讲,finalize()是在对象被GC回收前会调用的方法,而System.gc()强制GC开始回收工作纠正,不是强制,是建议,具体执行要看GC的意思简单地说,调用了 System.gc() 之后,java 在内存回收过程中就会调用那些要被回收的对象的 finalize() 方法。
通过上面的介绍可以发现正常情况下是不需要Override finalize()方法的,只有在你的程序中使用JNI技术时,需要你在调用JNI代码的Java Object的finalize方法中调用底层代码的释放资源的代码或析构函数之类的释放JVM Heap管理之外的底层代码内存资源。这种情况在Android之类的内存较小的嵌入式系统中更加常见。
finalize()方法与C++析构函数的区别
程序员都了解初始化的重要性,但常常会忘记同样重要的清除工作。毕竟,谁需要清除一个int 呢?但在使用程序库时,把一个对象用完后就“弃之不顾”的做法并非总是安全的。当然,Java有垃圾回收器来回收无用对象占据的内存资源。但也有特殊情况:假定你的对象(并非使用 new)获得了一块“特殊”的内存区域,由于垃圾回收器只知道释放那些经由 new分配的内存,所以它不知道该如何释放该对象的这块 “特殊”内存。
为了应对这种情况,Java允许你在类中定义一个名为finalize( )的方法。它的工作原理“应该”是这样的:一旦垃圾回收器准备好释放对象占用的存储空间,将首先调用其 finalize( )方法,并且在下一次垃圾回收动作发生时,才会真正回收对象占用的内存。所以要是你打算用 finalize( ),就能在“垃圾回收时刻”做一些重要的清除工作。
这里有一个潜在的编程陷阱,因为有些程序员(特别是 C++程序员)刚开始可能会误把finalize( )当作C++中的“析构函数”(C++中销毁对象必须用到这个函数)。所以有必要明确区分一下:在 C++中,对象一定会被“销毁”(如果程序中没有错误的话);而 Java 里的对象却并非总是被“垃圾回收”的。或者换句话说:
1. 对象可能不被回收。
2. 垃圾回收并不等于“析构”。
牢记这些,你就能远离困扰。这意味着在你不再需要某个对象之前,如果必须执行某些动作,那么你得自己去做。Java并未提供“析构函数”或相似的概念,要做类似的清除工作,你必须自己动手创建一个执行清除工作的普通方法。例如,假设某个对象在创建过程中,会将自己绘制到屏幕上。要是你不明确地从屏幕上将其擦除,它可能永远得不到清除。如果在
finalize( )里加入某种擦除功能,当“垃圾回收”发生时(不能保证一定会发生),finalize( )得到了调用,图像就会被擦除。要是“垃圾回收”没有发生,图像就会一直保留下来。
也许你会发现,只要程序没有濒临存储空间用完的那一刻,对象占用的空间就总也得不到释放。如果程序执行结束,并且垃圾回收器一直都没有释放你创建的任何对象的存储空间,则随着程序的退出,那些资源会全部交还给操作系统。这个策略是恰当的,因为垃圾回收本身也有开销,要是不使用它,那就不用支付这部分开销了。
finalize( )用途何在?
此时,你已经明白了不该将finalize( )作为通用的清除方法。那么,finalize( )的真正用途是什么呢?
这引出了要记住的第三点:
3.垃圾回收只与内存有关。
也就是说,垃圾回收器存在的唯一原因是为了回收程序不再使用的内存。所以对于与垃圾回收有关的任何行为来说(尤其是finalize( )方法),它们也必须同内存及其回收有关。
但这是否意味着要是对象中含有其他对象,finalize( )就应该明确释放那些对象呢?不——无论对象是如何创建的,垃圾回收器都会负责释放对象占据的所有内存。这就将对 finalize( )的需求限制到特殊情况之下:你通过某种非“创建对象”的方式为对象分配了存储空间。不过,你也看到了,Java中一切皆为对象,那这种特殊情况是怎么回事呢?
看来之所以要有finalize( ),是由于你可能在分配内存时,采用了类似C语言中的做法而非Java中的通常做法。这种情况主要发生在使用“本地方法”的情况下,它是在Java中调用非Java代码的一种方式。本地方法目前只支持C和C++。但它们可以调用其它语言写的代码,所以你实际上可以调用任何代码。在非Java代码中,也许会调用类似C的malloc( )函数,用它分配存储空间,而且除非调用了free( )函数,否则存储空间将不会得到释放,从而造成内存泄露。当然,free( )是C和C++中的函数,所以你需要在finalize( )中用本地方法调用它。
至此,你或许已经明白了不要过多地使用finalize( )的道理了。对,它确实不是进行普通的清除工作的合适场所。那么,普通的清除工作应该在哪执行呢?
你必须执行清除
为清除一个对象,用户必须在进行清除的时刻调用执行清除动作的方法。听起来似乎很简单,但却与C++中的“析构函数”的概念稍有抵触。在C++ 中,所有对象都会被销毁,或者说, “应该”被销毁。如果在C++中创建了一个局部对象(就是在堆栈上创建,Java中可不行),此时的销毁动作发生在以“右花括号”为边界的、此对象作用域的末尾处进行。如果对象是
用new创建的(类似于Java),那么当程序员调用C++的delete( )时(Java没有这个命令),就会调用相应的析构函数。如果程序员忘了,那么永远不会调用析构函数,就会出现内存泄露,对象的其他部分也不会得到清除。这种错误很难跟踪,这也是让 C++程序员转向 Java的一个主要因素。
相反,Java不允许创建局部对象,你必须使用new。在Java中,也没有“delete”来释放对象,因为垃圾回收器会帮助你释放存储空间。甚至可以肤浅地认为,正是由于垃圾收集机制的存在,使得 Java 没有析构函数。然而,随着学习的深入,你就会明白垃圾回收器的存在并不能完全代替析构函数。(而且你绝对不能直接调用finalize( ),所以这也不是一个恰当的
途径。)如果你希望进行除释放存储空间之外的清除工作,你还是得明确调用某个恰当的Java方法。这就等同于使用析构函数了,而且没有它方便。
记住,无论是“垃圾回收”还是“终结”,都不保证一定会发生。如果Java虚拟机(JVM)并未面临内存耗尽的情形,它是不会浪费时间在回收垃圾以恢复内存上的。
终结条件
通常,你不能指望finalize( ),你必须创建其它的“清除”方法,并且明确地调用它们。看来,finalize( )只能存在于程序员很难用到的一些晦涩用法里了。不过,finalize( )还有一个有趣的用法,它并不依赖于每次都要对finalize( )进行调用,这就是对象“终结条件”的验证。
当你对某个对象不再感兴趣,也就是它可以被清除时,这个对象应该处于某种状态,使它占用的内存可以被安全地释放。例如,要是对象代表了一个打开的文件,在对象被回收前程序员应该关闭这个文件。只要对象中存在没有被适当清除的部分,你的程序就存在很隐晦的错误。finalize( )的价值在于可以用来最终发现这种情况,尽管它并不总是会被调用。如果某次
finalize( )的动作使得bug被发现,那你就可据此找出问题所在——这才是你真正关心的。
http://blog.csdn.net/jackie03/article/details/7327976
System.gc的更多相关文章
- 实战Java虚拟机之四:提升性能,禁用System.gc() ?
今天开始实战Java虚拟机之四:"禁用System.gc()". 总计有5个系列 实战Java虚拟机之一“堆溢出处理” 实战Java虚拟机之二“虚拟机的工作模式” 实战Java虚拟 ...
- java: system.gc()和垃圾回收机制finalize
System.gc()和垃圾回收机制前的收尾方法:finalize(收尾机制) 程序退出时,为每个对象调用一次finalize方法,垃圾回收前的收尾方法 System.gc() 垃圾回收方法 clas ...
- System.gc()与Object.finalize()的区别
finalize()是由JVM自动调用的,你可以用System.gc(),但JVM不一定会立刻执行,JVM感觉内存空间有限时,才会开始执行finalize(),至于新的对象创建个数和被收集个数不同是因 ...
- finalize与System.gc()
finalize Finalize是Object类的一个方法,可以用来被重写 finalize的工作原理应该是这样的:一旦垃圾收集器准备好释放对象占用的存储空间,它首先调用finalize(),而且只 ...
- 由dubbo服务禁用system.gc而引起的思考
我一直都有一个疑问,丰巢业务服务的生产环境jvm参数设置是禁止system.gc的,也就是开启设置:-XX:+DisableExplicitGC,但是生产环境却从来没有出现过堆外内存溢出的情况.说明一 ...
- (转)调用System.gc没有立即执行的解决方法
调用System.gc没有立即执行的解决方法 查看源码 当我们调用System.gc()的时候,其实并不会马上进行垃圾回收,甚至不一定会执行垃圾回收,查看系统源码可以看到 /** * Indicate ...
- void java.lang.System.gc()
void java.lang.System.gc() Runs the garbage collector. Calling the gc method suggests that the Java ...
- System.gc()和-XX:+DisableExplicitGC启动参数,以及DirectByteBuffer的内存释放
首先我们修改下JVM的启动参数,重新运行之前博客中的代码.JVM启动参数和测试代码如下: -verbose:gc -XX:+PrintGCDetails -XX:+DisableExplicitGC ...
- 浅谈System.gc()
今天巩固给大家讲讲System.gc().Java的内存管理着实给各位编程者带来很大的方便,使我们不再需要为内存分配烦太多神.那么讲到垃圾回收机制,就不得不讲讲System.gc(). 先简单 ...
随机推荐
- PROTEL99 SE生成的gerber 与ncdrill的坐标不对应
导入cam350后的: 解决方法:出gerber的时候在高级选项里面. 1.数据单位及格式 2.优化设置
- jqGrid一些操作
formatter:function(cellvalue,options,rowObject){} 在格式化行的时候这三个参数 cellvalue行数, options配置信息, rowObject行 ...
- 转:shell比较两个字符串是否相等
比较两个字符串是否相等的办法是: if [ "$test"x = "test"x ]; then这里的关键有几点:1 使用单个等号2 注意到等号两边各有一个空格 ...
- web.py入门
官网介绍: web.py is a web framework for Python that is as simple as it is powerful. web.py is in the pub ...
- BZOJ 1029: [JSOI2007]建筑抢修
1029: [JSOI2007]建筑抢修 Description 小刚在玩JSOI提供的一个称之为“建筑抢修”的电脑游戏:经过了一场激烈的战斗,T部落消灭了所有z部落的入侵者.但是T部落的基地里已经有 ...
- VS2010/MFC对话框二:为对话框添加控件)
为对话框添加控件 创建对话框资源需要创建对话框模板.修改对话框属性.为对话框添加各种控件等步骤,前面一讲中已经讲了创建对话框模板和修改对话框属性,本节继续讲如何为对话框添加控件. 上一讲中创建了一个名 ...
- poj 1068 Parencodings(栈)
题目链接:http://poj.org/problem?id=1068 思路分析:对栈的模拟,将栈中元素视为广义表,如 (((()()()))),可以看做 LS =< a1, a2..., a1 ...
- 第一种:NStread
- (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view, typica ...
- [置顶] linux常用命令手册
前言:整理了一份linux常用命令手册,与大家分享.这是一些比较常用的命令. 我已经整理成一份PDF带书签的手册,可以到CSDN免费下载. 下载地址:http://download.csdn.net/ ...
- Java线程(十):CAS
前言 在Java并发包中有这样一个包,java.util.concurrent.atomic,该包是对Java部分数据类型的原子封装,在原有数据类型的基础上,提供了原子性的操作方法,保证了线程安全.以 ...