JAVA内存管理与GC机制

来源:互联网 发布:mysql 误删除表恢复 编辑:程序博客网 时间:2024/06/05 11:22

一、内存管理

1、方法区(Method Area)

方法区(Method Area)与Java堆一样,是各个线程共享的内存区域,它用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。虽然Java虚拟机规范把方法区描述为堆的一个逻辑部分,但是它却有一个别名叫做Non-Heap(非堆),目的应该是与Java堆区分开来。

2、程序计数器(Program Counter Register)

程序计数器(Program Counter Register)是一块较小的内存空间,它的作用可以看做是当前线程所执行的字节码的行号指示器。在虚拟机的概念模型里(仅是概念模型,各种虚拟机可能会通过一些更高效的方式去实现),字节码解释器工作时就是通过改变这个计数器的值来选取下一条需要执行的字节码指令,分支、循环、跳转、异常处理、线程恢复等基础功能都需要依赖这个计数器来完成。
下面重点解下Java内存管理中的栈和堆。

3、栈(Stacks)

在Java中,JVM中的栈记录了线程的方法调用。每个线程拥有一个栈。在某个线程的运行过程中,如果有新的方法调用,那么该线程对应的栈就会增加一个存储单元,即帧(frame)。在frame中,保存有该方法调用的参数、局部变量和返回地址。
Java的参数和局部变量只能是基本类型的变量(比如int),或者对象的引用(reference)。因此,在栈中,只保存有基本类型的变量和对象引用。引用所指向的对象保存在堆中。(引用可能为Null值,即不指向任何对象)。
当被调用方法运行结束时,该方法对应的帧将被删除,参数和局部变量所占据的空间也随之释放。线程回到原方法,继续执行。当所有的栈都清空时,程序也随之运行结束。

本地方法栈与虚拟机栈的区别:

本地方法栈(Native Method Stacks)与虚拟机栈所发挥的作用是非常相似的,其区别不过是虚拟机栈为虚拟机执行Java方法(也就是字节码)服务,而本地方法栈则是为虚拟机使用到的Native Method服务。
什么是Native Method?简单地讲,一个Native Method就是一个java调用非java代码的接口。一个Native Method是这样一个java的方法:该方法的实现由非java语言实现,比如C。这个特征并非java所特有,很多其它的编程语言都有这一机制,比如在C++中,你可以用extern "C"告知C++编译器去调用一个C的函数。

4、堆(Heap)

堆是JVM中一块可自由分配给对象的区域。当我们谈论垃圾回收(garbage collection)时,我们主要回收堆(heap)的空间。
Java的普通对象存活在堆中。与栈不同,堆的空间不会随着方法调用结束而清空。因此,在某个方法中创建的对象,可以在方法调用结束之后,继续存在于堆中。这带来的一个问题是,如果我们不断的创建新的对象,内存空间将最终消耗殆尽。

二、GC机制
GC大致流程:找出堆中的活对象-->释放死对象的资源-->定期调整活对象的位置
常用的垃圾收集算法:
1.引用计数器(古老级)
优点:快速
缺点:无法检测出循环引用
关于引用,JAVA中分为四类:强引用,软引用,弱引用,虚引用
强引用:也不回收,如 A  a=new A();
软引用:SoftReference,当内存不足时回收,主要用于类似缓存等场景。
A  a=new A();
SoftReference<A> sf=newSoftReference<A>(a);
a=null;
sf.get();//如果被回收了,这时返回null;
弱引用:WeakReference,第二次垃圾回收时回收,主要用于监控对象是否被垃圾收集器标记为即将回收的垃圾,可以通过方法
isEnQueued返回对象是否被标记。
虚引用:垃圾回收时回收,无法通过引用取到对象值,重要用于检测对象是否从内存中删除。
2.Mark-Sweep(标记-清除算法)
最基础的垃圾回收算法,因为最容易实现,思想最简单。
标记阶段:标记出所有需要回收的对象
清除阶段:回收被标记对象所占用的空间
缺点:容易产生碎片,可能会导致后续大对象分配空间时无法找到而提前开始新一次GC
3.Copying(复制算法)
解决Mark-Sweep算法的缺陷,将内存划分为容量相等的两块,每次只使用一块,一块使用完了,将存活的对象复制到另一块上,
再把已使用的空间一次性清理,不容易出现内存碎片。
特点:虽然简单高效不容易产生碎片,但却付出了高昂的内存代价(可用内存减半)。
Copying的效率跟存活对象的数目有很大关系,存活对象多的话效率降低。
4.Mark-Compact(标记-整理算法)
解决Copying算法的缺陷,充分利用内存空间。
标记后,将存活对象向一端移动,清理掉边界以外的内存。
5.Generational Collection(分代收集)
目前大部分JVM采用的算法。
核心思想:根据对象存活的生命周期将内存划分为若干个不同的区域,一般将堆区划分为老年代(Tenured Generation)和新生代(Young Generation)
新生代:Copying算法,较大的Eden空间和两个较小的Survivor空间(比例大概8:1:1),回收时,将Eden和一个Survivor上的存活
对象复制到另一个Survivior上。
老年代:Mark-Compact算法
堆区之外的永久代,主要存储class类,常量,方法描述等,对永久代主要回收废弃常量、无用的类。
三、典型的垃圾收集器
算法是基础,收集器是具体实现。
新生代的GC叫做Minor GC,发生频率高;当survivor1不足以存放Eden和Survivor0的存活对象时,将存活对象放到老年代,老年代
满了会触发一次FUll GC,即Major GC。新生代中经历N次回收仍存活的对象会放到老年代。
内存分配:新生代:老年代=1:2。
1.Serial/Serial Old
最古老、单线程收集器,收集时暂停用户线程,优点是简单高效
Serial:新生代,Copying算法
Serial Old:老年代,Mark-Compact算法
2.ParNew
Serial的多线程版本
3.Parallel Scavenge
新生代的多线程(并行)收集器,Server模式,不需要暂停其他用户,Copying算法,主要是为了达到可控的吞吐量,相对于
ParNew多了自适应调节。
4.Parallel Old
是Parallel Scavenge的老年代版本,Mark-compact算法
5.CMS(Current Mark Sweep)

过程如下:初始标记,并发标记,重新标记,并发清除,优点是并发收集,低停顿,缺点是对CPU资源非常敏感,无法处理浮动垃圾,收集结束会产生大量空间碎片。

老年代收集器,Mark-Sweep算法,并发,获取最短回收时间。默认搭配ParNew为新生代收集器,Serial Old为备选老年代收集器。
6.G1
G1收集器是基于标记整理算法实现的,不会产生空间碎片,可以精确地控制停顿,将堆划分为多个大小固定的独立区域,并跟踪这
些区域的垃圾堆积程度,在后台维护一个优先列表,每次根据允许的收集时间,优先回收垃圾最多的区域(Garbage First)。
并发+并行


参考文章: JAVA内存管理http://blog.csdn.net/u013142781

Java虚拟机学习-垃圾收集器http://blog.csdn.net/java2000_wl/article/details/8030172



原创粉丝点击