JVM内存管理和垃圾回收机制介绍

http://backend.blog.163.com/blog/static/20229412620128233285220/

内存管理和垃圾回收机制是JVM最核心的两个组成部分,对其内部实现的掌握是Java开发人员开发出高质量的Java系统的必备条件。最近整理了一些关于JVM内存管理和垃圾回收方面的知识,这里梳理一下,分享给大家,希望能够对Java虚拟机有更深入的了解。

1. JVM内存管理

首先,JVM将内存组织为主内存和工作内存两个部分。主内存中主要包括本地方法区和堆。每个线程都有一个工作内存,工作内存中主要包括两个部分,一个是属于该线程的栈和对主存部分变量拷贝的寄存器。

图1 JVM内存模型

每一个JVM实例都有一个方法区和堆,被该实例内的所有线程共享,而每一个新的线程启动时,JVM都会为该线程分配一个仅属于该线程的工作内存。Java中主要有3种类型的变量,JVM对内存的组织管理映射到变量的存储上,可以按照如下理解:

1)  静态变量,被同一个类中的多个对象共享,存储在方法区中,被所有线程共享;

2)  实例变量,声明在类中,存储在堆上;

3)  局部变量,声明在方法内,存储在栈上;

每个线程只能操作自己的工作内存,线程不能直接操作主存,除非变量用volatile关键字修饰,工作内存之间不能直接访问,必须通过主内存。

2. 垃圾回收机制

Java的垃圾回收是由JVM来完全负责的,对用户来说基本上是透明的。之所以说基本,是因为:

1) 用户可以使用Finalize函数在垃圾对象回收之前,释放由本地方法申请的内存空间或者关闭文件等操作。

2) 用户可以使用System.gc()建议JVM进行垃圾回收,但需要注意的是,这仅仅是建议,至于什么时候回收,会不会回收都是未知的,都是由JVM来完全负责的。

JVM以独立的,低优先级的线程来对堆空间进行检测。整个垃圾回收机制主要涉及两个根本问题:其一是要确定到底什么是垃圾。其二要是释放垃圾对象占用的内存空间。

首先我们先来看第一问题,Java中没有被其他对象引用的对象即为垃圾对象,Java中有四种类型的对象引用:StrongReference, SoftReference, WeakReference, PhantomReference.

1) StrongReference:即Java普通的对象引用。

2) SoftReference:如果一个对象被SoftReference引用,又没有其他的StrongReference指向它的话,该对象可以在任何时候被垃圾回收,但这个时候一般发生在其他线程因为内存空间不足被挂起,发生OutofMemory错误之前。SoftReference一般用于Cache内对象的引用。

3) WeakReference:如果一个对象被WeakReference引用,又没有其他的StrongReference和SoftReference引用指向他的话,该对象会被立即回收。相对于SoftReferen ce而言,WeakReference是一种急切的垃圾回收。

4)PhantomReference:如果一个对象被PhantomReference引用,而没有其他的StrongReference, SoftReference和WeakReference引用指向它的话,该对象可以在任何时候被垃圾回收。PhantomReference引用一般用于检测对象进入ReferenceQueue,用于没有实现Finalize方法的对象在垃圾回收前释放由本地方法申请的内存空间和关闭文件的操作。

在了解了到底什么是垃圾之后,我想介绍一下JVM的垃圾回收算法。目前,JVM主流的垃圾回收算法是一种叫做分代垃圾回收的算法。我觉得采用这种算法一个立足点在于:JAVA中对大多数对象都存活期较短,能长时间存在的对象占少数。正是基于这样一个事实,我觉得才有必要对JAVA的主存空间按照对象的存活期进行划分。

分代垃圾回收算法主要包括以下三个回收算法:复制收集,Mark-Sweep,Mark-Compacting。

1) 复制收集:将一个区域中可达的对象复制到另外一个空闲的区域中,将原区域中所有内容擦除。复制收集的优点在于其效益较高,缺点在于需要一块额外的内存空间。所以适用于小型的垃圾回收。JVM中的MajorGC普遍采用该种算法。

2) Mark-Sweep:叫做标记清除收集,首先对区域中所有引用进行扫描,将不可达的对象标记出来,然后将这些标记的对象擦除。Mark-Sweep的优点在于不需要移动元素,就可以实现垃圾回收,同时也不需要额外的空闲内存区域。缺点是会产生大量的内存碎片。同时Mark-Sweep的内存分配采用的是一种Freelist的方式,就是将所有的空间区域做成一个List,每次分配空间就是从FreeList中去找一个满足条件的区域分配。这种分配方式效率低,而且复杂。

3) Mark-Compacting叫做标记压缩收集,首先对整个空间进行扫描,标记出可达对象和不可达对象,然后将不可达的对象擦除,将可达的对象移动到一个连续的空间中。这种算法的优点在于不会产生内存碎片。它和复制收集的内存分配都采用的是一种叫做空闲指针的方式,就是将指针指向最后一个对象的空间,每次直接从后面的空间进行分配。这种内存分配方法效率高。Mark-Compacting的缺点是需要移动元素。同时,除了标记不可达对象,还要标记可达的对象。效率较低。Mark-Sweep和Mark-Compacting主要用于旧生代内存的回收。

Java的主存区空间主要划分为3个部分:新生代(Young),旧生代(Tenured)和Perm区。新生代又划分为3个部分:Eden和两个Survivor区。所有用New创建的对象都是放在Eden区的,Survivor区主要是用于复制收集的目标空间。所以两个Survivor始终至少有一个是空闲的。

图2 JVM代划分示意图

新生代的垃圾回收算法采用的是复制收集,在我看来原因就是复制收集效率高。但是需要额外的内存空间,这个内存空间就是Survivor。新生代的垃圾回收叫做ManorGC,JVM提供了三种新生代的垃圾回收器:

1)串行GC:用一个线程完成整个复制收集过程,需要应用程序暂停执行。在单CPU的情况下,效率高于其他的两种。但是会造成应用程序出现长时间的暂停。

2) 并行GC:用多线程来完成整个复制收集过程,会降低应用程序暂停的时间。

3) ParNew:与并行GC基本相同,唯一的不同在于设计ParNew主要是用来配合旧生代CMS并发收集使用的。在旧生代的CMS过程中,有可能新生代出发ManorGC,改变对象的引用关系,从而造成旧生代的错误,ParNew就是专门处理该情况的。加了特殊的处理,保证了对象关系的正确性。所以旧生代的CMS必须配合新生代的ParNew一起。

旧生代的垃圾回收算法主要采用的是Mark-Sweep和Mark-Compacting,JDK提供了三种旧生代的垃圾回收器:

1) 串行GC:该回收器采用的是Mark-Compacting算法,用一个单独的线程完成标记和压缩过程,需要长时间暂停应用程序的执行,在单CPU的情况下,效率要高于其他两种。

2)并行GC:该回收器采用的并行的Mark-compacting算法,首先,用多线程去标记可达对象和不可达对象,用一个线程去确定移动的目标区域和源区域,在用多线程并发移动压缩。优点是并发执行,能够减少应用程序的暂停时间,缺点是移动元素,效率不高。

3)CMS GC:并发GC,采用的是Mark-Sweep算法,首先用一个线程去标记直接可达的对象。然后使用多线程去标记间接可达的对象。由于第二阶段是并发标记,所以有可能在标记过程中,应用修改了对象的引用关系,所以第三步是用单线程来对修改的引用关系进行标记。最后用多线程进行清除。该种算法的优点由于绝大多数都是并发过程,所以能很大程度上降低应用的暂停时间,但是缺点是3次标记,总的时间要长于并行GC。

时间: 2024-10-02 16:35:59

JVM内存管理和垃圾回收机制介绍的相关文章

详解JVM内存管理与垃圾回收机制 (上)

Java应用程序是运行在JVM上的,得益于JVM的内存管理和垃圾收集机制,开发人员的效率得到了显著提升,也不容易出现内存溢出和泄漏问题.但正是因为开发人员把内存的控制权交给了JVM,一旦出现内存方面的问题,如果不了解JVM的工作原理,将很难排查错误.本文将从理论角度介绍虚拟机的内存管理和垃圾回收机制,算是入门级的文章,希望对大家的日常开发有所助益. 一.内存管理 也许大家都有过这样的经历,在启动时通过-Xmx或者-XX:MaxPermSize这样的参数来显式的设置应用的堆(Heap)和永久代(P

Java之美[从菜鸟到高手演变]之JVM内存管理及垃圾回收

很多Java面试的时候,都会问到有关Java垃圾回收的问题,提到垃圾回收肯定要涉及到JVM内存管理机制,Java语言的执行效率一直被C.C++程序员所嘲笑,其实,事实就是这样,Java在执行效率方面确实很低,一方面,Java语言采用面向对象思想,这也决定了其必然是开发效率高,执行效率低.另一方面,Java语言对程序员做了一个美好的承诺:程序员无需去管理内存,因为JVM有垃圾回收(GC),会去自动进行垃圾回收. 其实不然: 1.垃圾回收并不会按照程序员的要求,随时进行GC. 2.垃圾回收并不会及时

V8 内存管理和垃圾回收机制总结

这篇文章主要介绍 V8 的内存管理和垃圾回收知识. V8 内存管理及垃圾回收机制浅析 由于 V8 引擎的原因,Node 在操作大内存对象时受到了一些限制,在 64 位的机器上,默认最大操作的对象大小约为 1.4G,在 32 位的机器上,默认最大操作的对象大小约为 0.7G. 如果我们的 Node 程序会经常操作一些大内存的对象,可以对这个默认值进行修改: node --max-old-space-size=1700 index.js node --max-new-space-size=1024

Java性能剖析]Sun JVM内存管理和垃圾回收

内存管理和垃圾回收是JVM非常关键的点,对Java性能的剖析而言,了解内存管理和垃圾回收的基本策略非常重要.本篇对Sun JVM 6.0的内存管理和垃圾回收做大概的描述. 1.内存管理      在程序运行过程当中,会创建大量的对象,这些对象,大部分是短周期的对象,小部分是长周期的对象,对于短周期的对象,需要频繁地进行垃圾回收以保证无用对象尽早被释放掉,对于长周期对象,则不需要频率垃圾回收以确保无谓地垃圾扫描检测.为解决这种矛盾,Sun JVM的内存管理采用分代的策略.      1)年轻代(Y

.Net程序的内存管理和垃圾回收机制

.NET 内存管理和垃圾回收 C/C++ 程序需要开发者手动分配和释放内存,.Net程序则使用垃圾回收技术自动收集不再使用的内存.垃圾回收器(GC)使用引用 跟踪占用内存的对象,如果对象被设置为null或已不在使用范围,GC就会标志该对象为可回收,这样GC就可以回收被这些对象占用的内存. 垃圾回收器(GC)使用Win32? VirtualAlloc() 接口为自己的堆分配内存,.Net托管堆是一个巨大连续的虚拟内存.GC先预留虚拟内存,当托管堆增长时则提交内存.GC跟踪托管堆末尾可用的地址并把下

JVM的生命周期、体系结构、内存管理和垃圾回收机制

一.JVM的生命周期 JVM实例:一个独立运行的java程序,是进程级别 JVM执行引擎:用户运行程序的线程,是JVM实例的一部分 JVM实例的诞生 当启动一个java程序时.一个JVM实例就诞生了,任何一个拥有public static void main(string[] args)的函数都可以作为实例的运行启点 2.  JVM实例运行 main作为程序初始化线程的起点,任何其他线程由其启动. JVM有两种线程:守护线程和非守护线程.守护线程由JVM使用.main启动后将是非守护线程. 3.

JVM内存管理及垃圾回收

一.JVM内存的构 Java虚拟机会将内存分为几个不同的管理区,这些区域各自有各自的用途,根据不同的特点,承担不同的任务以及在垃圾回收时运用不同的算法.总体分为下面几个部分: 程序计数器(Program Counter Register).JVM虚拟机栈(JVM Stacks).本地方法栈(Native Method Stacks).堆(Heap).方法区(Method Area) 如下图: 1.程序计数器(Program Counter Register) 这 是一块比较小的内存,不在Ram上

【java_基础】JVM内存模型和垃圾回收机制

1. JVM内存模型 Java虚拟机在程序执行过程会把jvm的内存分为若干个不同的数据区域来管理,这些区域有自己的用途,以及创建和销毁时间. 先来看一下Java程序具体执行的过程 上图中的运行数据区(Runtime Data Areas)即为JVM内存区域,其结构如下图: 各区域存储的具体信息: 1.1 程序计数器 程序计数器(Program Counter Register),也有称作为PC寄存器.JVM中的程序计数器跟汇编语言中的程序计数器在功能上是相同的,即指示待执行指令的地址.当 CPU

JAVA内存管理和垃圾回收机制

JVM内存组成结构 JVM栈由堆.栈.本地方法栈.方法区等部分组成,结构图如下所示: 1)堆 所有通过new创建的对象的内存都在堆中分配,其大小可以通过-Xmx和-Xms来控制.堆被划分为新生代和旧生代,新生代又被进一步划分为Eden和Survivor区,最后Survivor由From Space和To Space组成,结构图如下所示: 新生代.新建的对象都是用新生代分配内存,Eden空间不足的时候,会把存活的对象转移到Survivor中,新生代大小可以由-Xmn来控制,也可以用-XX:Surv