`

Java内存回收 垃圾回收机制

 
阅读更多

一、内存分配策略

一般内存粗糙的可以分为两块,堆heap空间和栈stack空间。但其实Java内存远比这个复杂,只是堆和栈是最重要的两块。
其中栈,也成为局部变量表,主要存储的是在非static的自动变量、函数参数、表达式的临时结果金额函数返回值。栈是线程私有的,生命周期与线程相同。
而堆heap是内存中最大的一块,为线程共享,JVM启动时创建。主要用于存放new出来的对象以及数组。而JAVA的垃圾回收机制管理的往往也就是这个堆内存,因此也称为GC(Garbage Collection Heap)堆。
此外还有方法区和常量池等内存区域。

二、自动垃圾回收机制

对于堆的管理,不同的语言实现方式是不同的。其中C语言是通过库函数malloc()和free()来实现。而C++直接将对堆空间中对象的操作和分配释放到语言层次,使用new和delete语句。而Java只需要开发人员在需要时候创建就可以了,何时释放都由JVM来控制。而在Java的Object类中有一个finalize()方法,在垃圾回收器真正回收之前调用。
Java的垃圾回收机制最为Java语言的一大特性,将Java堆空间内存的释放交给JVM自动处理,无须开发人员在程序中显示调用,从而避免了因为开发人员忘记释放内存而造成的内存溢出。

————————————————————————————————————————————————
先看一看 JVM的内部结构——


如图所示,JVM主要包括两个子系统和两个组件。两个子系统分别是Class loader子系统和Execution engine(执行引擎) 子系统;两个组件分别是Runtime data area (运行时数据区域)组件和Native interface(本地接口)组件。

Class loader子系统的作用:根据给定的全限定名类名(如 java.lang.Object)来装载class文件的内容到 Runtime data area中的method area(方法区域)。Java程序员可以extends java.lang.ClassLoader类来写自己的Class loader。  

Execution engine子系统的作用:执行classes中的指令。任何JVM specification实现(JDK)的核心都是Execution engine,不同的JDK例如Sun 的JDK 和IBM的JDK好坏主要就取决于他们各自实现的Execution engine的好坏。

Native interface组件:与native libraries交互,是其它编程语言交互的接口。当调用native方法的时候,就进入了一个全新的并且不再受虚拟机限制的世界,所以也很容易出现 JVM无法控制的native heap OutOfMemory。

Runtime Data Area组件:这就是我们常说的JVM的内存了。它主要分为五个部分——
1、Heap (堆):一个Java虚拟实例中只存在一个堆空间
2、 Method Area(方法区域):被装载的class的信息存储在Method area的内存中。当虚拟机装载某个类型时,它使用类装载器定位相应的class文件,然后读入这个class文件内容并把它传输到虚拟机中。
3、 Java Stack(java的栈):虚拟机只会直接对Java stack执行两种操作:以帧为单位的压栈或出栈
4、Program Counter(程序计数器):每一个线程都有它自己的PC寄存器,也是该线程启动时创建的。PC寄存器的内容总是指向下一条将被执行指令的饿地址,这里 的地址可以是一个本地指针,也可以是在方法区中相对应于该方法起始指令的偏移量。  
5、Native method stack(本地方法栈):保存native方法进入区域的地址

以上五部分只有Heap 和Method Area是被所有线程的共享使用的;而Java stack, Program counter 和Native method stack是以线程为粒度的,每个线程独自拥有自己的部分。

了解JVM的系统结构,再来看看JVM内存回收问题了——
Sun的 JVM Generational Collecting(垃圾回收)原理是这样的:把对象分为年青代(Young)、年老代(Tenured)、持久代(Perm),对不同生命周期的对象 使用不同的算法。(基于对对象生命周期分析)


如上图所示,为Java堆中的各代分布。  
1. Young(年轻代)
年轻代分三个区。一个Eden 区,两个Survivor区。大部分对象在Eden区中生成。当Eden区满时,还存活的对象将被复制到Survivor区(两个中的一个),当这个 Survivor区满时,此区的存活对象将被复制到另外一个Survivor区,当这个Survivor去也满了的时候,从第一个Survivor区复制 过来的并且此时还存活的对象,将被复制年老区(Tenured。需要注意,Survivor的两个区是对称的,没先后关系,所以同一个区中可能同时存在从 Eden复制过来 对象,和从前一个Survivor复制过来的对象,而复制到年老区的只有从第一个Survivor去过来的对象。而且,Survivor区总有一个是空 的。  
2. Tenured(年老代)
年老代存放从年轻代存活的对象。一般来说年老代存放的都是生命期较长的对象。  
3. Perm(持久代)
用于存放静态文件,如今Java类、方法等。持久代对垃圾回收没有显著影响,但是有些应用可能动态生成或者调用一些 class,例如Hibernate等,在这种时候需要设置一个比较大的持久代空间来存放这些运行过程中新增的类。持久代大小通过 -XX:MaxPermSize=进行设置。

举个例子:当在程序中生成对象时,正常对象会在年轻代中分配空间,如果是过大的对象也可能会 直接在年老代生成(据观测在运行某程序时候每次会生成一个十兆的空间用收发消息,这部分内存就会直接在年老代分配)。年轻代在空间被分配完的时候就会发起 内存回收,大部分内存会被回收,一部分幸存的内存会被拷贝至Survivor的from区,经过多次回收以后如果from区内存也分配完毕,就会也发生内 存回收然后将剩余的对象拷贝至to区。等到to区也满的时候,就会再次发生内存回收然后把幸存的对象拷贝至年老区。

通常我们说的JVM内 存回收总是在指堆内存回收,确实只有堆中的内容是动态申请分配的,所以以上对象的年轻代和年老代都是指的JVM的Heap空间,而持久代则是之前提到的 Method Area,不属于Heap。

了解完这些之后,以下的转载一热衷于钻研技术的哥们Richen Wang关于内存管理的一些建议——
1、手动将生成的无用对象,中间对象置为null,加快内存回收。
2、对象池技术 如果生成的对象是可重用的对象,只是其中的属性不同时,可以考虑采用对象池来较少对象的生成。如果有空闲的对象就从对象池中取出使用,没有再生成新的对 象,大大提高了对象的复用率。
3、JVM调优 通过配置JVM的参数来提高垃圾回收的速度,如果在没有出现内存泄露且上面两种办法都不能保证内存的回收时,可以考虑采用JVM调优的方式来解决,不过一 定要经过实体机的长期测试,因为不同的参数可能引起不同的效果。如-Xnoclassgc参数等。

推荐的两款内存检测工具
1、 jconsole JDK自带的内存监测工具,路径jdk bin目录下jconsole.exe,双击可运行。连接方式有两种,第一种是本地方式如调试时运行的进程可以直接连,第二种是远程方式,可以连接以服务 形式启动的进程。远程连接方式是:在目标进程的jvm启动参数中添加-Dcom.sun.management.jmxremote.port=1090 -Dcom.sun.management.jmxremote.ssl=false -Dcom.sun.management.jmxremote.authenticate=false 1090是监听的端口号具体使用时要进行修改,然后使用IP加端口号连接即可。通过该工具可以监测到当时内存的大小,CPU的使用量以及类的加载,还提供 了手动gc的功能。优点是效率高,速度快,在不影响进行运行的情况下监测产品的运行。缺点是无法看到类或者对象之类的具体信息。使用方式很简单点击几下就 可以知道功能如何了,确实有不明白之处可以上网查询文档。

2、JProfiler 收费的工具,但是到处都有破解办法。安装好以后按照配置调试的方式配置好一个本地的session即可运行。可以监测当时的内存、CPU、线程等,能具体 的列出内存的占用情况,还可以就某个类进行分析。优点很多,缺点太影响速度,而且有的类可能无法被织入方法,例如我使用jprofiler时一直没有备份 成功过,总会有一些类的错误。


分享到:
评论

相关推荐

    java 垃圾回收 机制详解

    经过半个世纪的发展,内存的动态分配与内存回收技术已经相当成熟,一切看起来都进入了“自动化”时代,那为什么我们还要去了解GC和内存分配呢?答案很简单:当需要排查各种内存溢出、内存泄漏问题时,当垃圾收集成为...

    java垃圾回收及内存泄漏.pptx

    java 垃圾回收机制 内存泄漏 技术分享 相关技术分享

    Java垃圾回收机制和内存分配

    你认真演示了一遍,你就能明白JAVA的垃圾回收机制。当然文档写的不一定全面,比如文档当中关于老年区少年区有一页写的不是很完整,我也没有添加太多进去,但是还是很有很全面很有参考意义的。

    Java内存与垃圾回收调优.docx

    Java内存与垃圾回收调优,Java内存与垃圾回收的调优是一个重要的主题,特别是在高性能和...理解垃圾回收机制: 包括标记-清除、复制、标记-压缩和分代收集等机制。 了解每种机制的优缺点以及它们在各种垃圾回收器中的使

    java 垃圾回收机制详细介绍

    它把程序员从手工回收内存空间的繁重工作中解脱了出来。在SUN公司的Java程序员(Java Programmer)认证考试中,垃圾收集器是必考的内容,一般最多可以占总分值的6%左右。但是由于SUN公司的Java Programming Language...

    Java内存回收机制

    一、Java对象在内存引用状态  内存泄露:程序运行过程中,会不断分配内存空间,那些不再使用...  强引用是Java编程中广泛使用的引用类型,被强引用所引用的Java对象绝不会被垃圾回收机制回收,即使系统内存紧张;即使

    Java内存管理机制

    1、JAVA 内存管理总结 2、Java的内存管理实例 3、垃圾回收机制:

    JAVA垃圾回收机制与内存泄露问题.docx

    JAVA垃圾回收机制与内存泄露问题.docx

    Java内存结构与垃圾回收机制算法分析_01.docx

    Java内存结构与垃圾回收机制算法分析

    java垃圾回收技术,面试会问

    值得一看的基础东西,java的垃圾回收机制,之前百度面试被问到

    JAVA垃圾回收机制与内存泄露问题实用.pdf

    JAVA垃圾回收机制与内存泄露问题实用.pdf

    Java垃圾回收详解

    垃圾回收 对象是使用new创建的, 但是并没有与之相对应的delete操作来回收对象占用的内存. 当我们完成对某个 对象的使用时, 只需停止该对象的引用: ->将引用改变为指向其他对象 ->将引用指向null ->从方法中返回, ...

    java垃圾回收(gc)机制详解.pdf

    哪些内存需要回收是垃圾回收机制第一个要考虑的问题,所谓“要回收的垃圾”无非就是那些不可能再被任何途径使用的对象。

    Java垃圾回收知识,垃圾回收资料

    准确理解垃圾回收的原理和机制,以及能够优化和调优垃圾回收过程的能力,是一个Java开发者必备的基本技能。 面试中可能会问到的垃圾回收相关问题: 什么是垃圾回收?Java中的垃圾回收是如何工作的? 有哪些垃圾回收...

    Java中内存泄露及垃圾回收机制.pdf

    Java 中内 存泄 露及 垃圾 回收机制 ,详细了解请参考《java编程思想》

    java垃圾回收以及jvm参数调优概述

    垃圾回收机制的引入可以有效的防止内存泄露、保证内存的有效使用,也大大解放了Java程序员的双手,使得他们在编写程序的时候不再需要考虑内存管理。本文主要对java垃圾回收机制以及jvm参数等方面做个综述,也算是...

    Java中内存泄露及垃圾回收机制参照.pdf

    Java中内存泄露及垃圾回收机制参照.pdf

    jvm内存模型以及垃圾回收机制.pptx

    jvm内存模型以及垃圾回收机制.pptx

    Java中内存泄露及垃圾回收机制宣贯.pdf

    Java中内存泄露及垃圾回收机制宣贯.pdf

    java垃圾回收机制1

    java垃圾回收机制标记清除算法介绍最主要的理论算法之一,在实践过程中,为了真实情景需要,需要许多调整。这会(终将会)导致内存碎片化,同样会导致磁盘碎片化,由此

Global site tag (gtag.js) - Google Analytics