为了保证java内存不会溢出,java中有垃圾回收机制。垃圾回收机制是指jvm用于释放那些不再使用的对象所占用的内存。java语言并不要求jvm有gc,也没有规定gc如何工作。垃圾收集的目的在于清除不再使用的对象。gc通过确定对象是否被活动对象引用来确定是否收集该对象。
内存溢出就是你要求分配的java虚拟机内存超出了系统能给你的,系统不能满足需求,于是产生溢出。
内存泄漏是指你向系统申请分配内存进行使用(new),可是使用完了以后却不归还(delete),结果你申请到的那块内存你自己也不能再访问,该块已分配出来的内存也无法再使用,随着服务器内存的不断消耗,而无法使用的内存越来越多,系统也不能再次将它分配给需要的程序,产生泄露。一直下去,程序也逐渐无内存使用,就会溢出。(内存泄露vs内存溢出)
【内存空间的划分】
Sun JDK实现时遵照JVM规范,将内存空间划分为方法区、堆、JVM方法栈、本地方法栈及pc寄存器。
* 方法区:
存放要加载的类or接口的信息(名称、修饰符等)、类的static变量、final常量、Field信息、方法信息(元数据)。通过Class对象获取的相关数据就来自该区域。 --见
Sun JDK中这块区域对应Permanet Generation(持久代),默认最小值为16MB,最大值为64MB,可通过-XX:PermSize及-XX:MaxPermSize来设置最小值和最大值。
* 堆(Heap Memory):
存放对象实例及数组值,Heap中对象所占用的内存由GC进行回收,在32位系统上最大为2G,64位系统上无限制。可通过-Xms和-Xmx控制,-Xms为JVM启动时申请的最小Heap内存,-Xmx为JVM可申请的最大Heap内存。
* 方法栈:
每个线程均会创建PC寄存器和方法栈。方法栈中有栈帧。方法栈为线程私有。当方法运行完毕,该方法对应的栈帧所占用的空间自动释放。
--想到
方法栈空间不足时,抛出StackOverflowError错误(如不合理的递归调用),在Sun JDK中可通过-Xss设置大小。
【Heap Memory详解】
{ [ (Eden)(S0)(S1) ] [ ( ) ] }
|----New Gen------| |-Old Gen--|
|---------------heap-----------------------|
-New Generation:新生代
大多数情况下java程序中新建的对象都从新生代分配内存,新生代由Eden Space和两块相同大小的Survivor Space构成。可通过-Xmn指定新生代的大小。
-Eden Space:创建对象,依据的是方法区中存放的类的元数据。
-S0:当Eden Space空间用完时,JVM的垃圾回收器对其进行回收,将Eden中不被其他对象使用的对象进行销毁,同时将Eden中还被其他对象引用的对象移到S0区,如果S0区也没有空间了则移动到S1区。
-S1
-Old Generation:旧生代
存放新生代中经过多次垃圾回收仍然存活的对象,例如缓存对象。旧生代占用大小为-Xmx值减-Xmn对应的值。
------Shallow Heap
java.lang.OutOfMemoryError: GC overhead limit exceeded
这个是JDK6新添的错误类型。是发生在GC占用大量时间而很少的堆被恢复的时候发生的,是一种保护机制。解决方案是,关闭该功能,使用 -XX:-UseGCOverheadLimit
Sun官方解释:
The parallel / concurrent collector will throw an OutOfMemoryError
if too much time is being spent in garbage collection: if more than 98% of the total time is spent in garbage collection and less than 2% of the heap is recovered, an OutOfMemoryError will be thrown. This feature is designed to prevent applications from running for an extended period of time while making little or no progress because the heap is too small. If necessary, this feature can be disabled by adding the option -XX:-UseGCOverheadLimit
to the command line
如何避免:查看是否有使用大内存的代码或死循环。
【System.gc() 与 finalize()】
java.lang.System.gc()
java.lang.Runtime.getRuntime().gc()
java.lang.Object.finalize()
一个题目:11. rbo = new ReallyBigObject();12. // more code here13. rbo = null;14.Which statement should be placed at line 14 to suggest that the virtualmachine expend effort toward recycling the memory used by theobject rbo?A. System.gc();B. Runtime.getRuntaime().gc();C. System.freeMemory();D. Runtime.getRuntime().growHeap();E. Runtime.getRuntime().freeMemory();
gc()有何用?
调用 gc 方法暗示着 Java 虚拟机做了一些努力来回收未用对象,以便能够快速地重用这些对象当前占用的内存。当控制权从方法调用中返回时,虚拟机已经尽最大努力从所有丢弃的对象中回收了空间。 调用 System.gc() 等效于调用Runtime.getRuntime().gc()
finalize()有何用?
gc 只能清除在堆上分配的内存(纯java语言的所有对象都在堆上使用new分配内存),而不能清除栈上分配的内存(当使用技术时,可能会在栈上分配内存,例如java调用c程序,而该c程序使用malloc分配内存时)。因此,如果某些对象被分配了栈上的内存区域,那gc就管不着了,对栈上的对象进行内存回收就要靠finalize()。举个例子来说,当java 调用非java方法时(这种方法可能是c或是c++的),在非java代码内部也许调用了c的malloc()函数来分配内存,而且除非调用那个了 free() 否则不会释放内存(因为free()是c的函数),这个时候要进行释放内存的工作,gc是不起作用的,因而需要在finalize()内部的一个固有方法调用free()。finalize的工作原理应该是这样的:一旦垃圾收集器准备好释放对象占用的存储空间,它首先调用finalize(),而且只有在下一次垃圾收集过程中,才会真正回收对象的内存.所以如果使用finalize(),就可以在垃圾收集期间进行一些重要的清除或清扫工作.
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() 方法。
【待看】
1.
2.
3.