java中JVM虚拟机内存模型详细说明

java中JVM虚拟机内存模型详细说明

2012-12-12 18:36:03|  分类: JAVA |  标签:java  jvm  堆内存  虚拟机  |举报|字号 订阅

JVM的内部结构如下图:

一个优秀Java程序员,必须了解Java内存模型、GC工作原理,以及如何优化GC的性能、与GC进行有限的交互,有一些应用程序对性能要求较高,例如嵌入式系统、实时系统等,只有全面提升内存的管理效率,才能提高整个应用程序的性能。

本文将从JVM内存模型、GC工作原理,以及GC的几个关键问题进行探讨,从GC角度提高Java程序的性能。

1 java内存分为:

程序计数器(当前线程所执行字节码的行号指示器,字节码解释器要通过改变这个计数器的值来选择下一条字节码指令,分支、循环、异常处理等。每条线程都有一条独立的程序计数器,属于线程私有的内存区)、

java虚拟机栈(也是线私有的,存储局部变量、操作栈,每个方法执行时创建一个栈帧,执行过程就是栈的出栈入栈操作)、

本地方法栈(执行native方法)、

年轻代堆(eden、from survivor、to survivor)、年老代堆(经过几次垃圾回收,保存下来的)、

持久代堆(也叫方法区,保存常量池和类型数据信息,不会被回收)、

直接内存(使用native方法直接分配堆外内存,再通过堆内的DirectByteBuffer作为这块内存的引用进行操作)

2 对象访问有两种:通过句柄池和直接通过指针,句柄池的好处是垃圾回收后,不需要改变对象引用,只要改变句柄引用;直接指针的好处是效率较高。

引用《深入java虚拟机第二章》

解释了minor gc和major gc,和两个survivor区之于复制收集算法的意义

3 jvm内存机制

java内存中的四种引用解析,强引用、弱引用、软引用、虚引用

4 垃圾回收算法

4.1 引用计数,效率高,但是无法解决无用对象循环引用的问题。及时引用计数大于0,但却不可用。

4.2 根对象可达,java虚拟机使用的方式。可以作为根对象的有:虚拟机栈帧中引用的对象,方法区中(持久区)类静态属性引用的对象(类静态属性在创建实例之前就已经为这个属性分配好空间,创建好实例,在所有的类实例中共享),方法区中常量引用的对象(比如常量字符串),本地方法栈中引用的对象(native方法)。

4.3 引用强度,强(即传统理解的引用,obj=new obj()),软(softReference,在内存溢出之前,GC会将这部分纳入回收范围,进行二次回收,如果还是没有足够内存,那么OOM),弱(weakReference,一旦GC进行回收,无论内存是否充足都会被回收),虚(有虚引用不会影响生命周期,无法通过虚引用来获得一个对象,唯一作用是对象被回收时能得到一个系统通知,PhantomReference)

4.4 对象在回收之前会调用仅有一次finalize(),可以在finalize中自救,把自己被引用。只有当对象覆盖finalize()方法才会被放入执行finalize()的队列。不建议使用这个,可以用try{} finally代替。

4.5 java回收算法

标记清除法,效率低下,会产生内存碎片。

复制收集算法,较为主流的算法,分区,eden,survivor,将未被回收的对象移动到survivor区,然后一次性清理eden区。这种算法适用于对象存活率不高的情况,要牺牲一部分分配空间,一般来说eden:survivor=8:1。

当对象存活率很高的时候,复制收集算法的效率会出现问题,所以有了标记-整理算法。把所有存活对象整理到一端,再把另一端直接清理掉。

分代算法,java内存一般都分为新生代和年老代,根据两个区域对象存活率的特点,分别采用了复制-收集算法和标记-清理(标记-整理)算法。

4.6 垃圾收集器

serial收集器,单线程收集。很可恶,会停掉其他用户线程。糟糕的用户体验。但是适用于client端,因为产生的垃圾量少,回收快,几十毫秒解决。

parNew收集器,和serial一样。区别是收集时采用多线程机制。

CMS收集器,可以让收集线程和用户线程并发执行,可惜的是作为年老代的收集器,只能和上面两个不靠谱的收集器一起配合使用。它是以最短停顿时间为目标的年老代收集器,使用mark-sweep算法(标记-清除)。回收步骤是初始标记(stop world),并发标记,重新标记(stop world),并发清除。stop world的时间很短,所以可以认为是用户线程并发的收集器。

parallel scavenge收集器,是并发收集器,可以和用户线程并存。可以通过参数控制吞吐量=用户线程时间/(用户时间+垃圾收集时间),前台程序适用低吞吐量,减少用户操作每次停顿的时间;而后台程序适用于高吞吐量,尽快完成后台的计算。也称作吞吐量优先收集器。该收集器还能设置自适应模式,会根据实际情况动态调整吞吐量。

serial old收集器和serial一样,用于年老代收集。使用标记整理算法。

parallel old收集器,用于和parallel scavenge配合使用的年老代收集器,是parallel scavenge的最佳搭档。

G1(garbage first)收集器,最前沿的,jdk1.6进入试用期。采用标记-整理算法,并且对新生区分区,分区有不同的优先级,优先收集内存较满的区域。有着很高的并发和较低的停顿,也算是敏捷的一种收集器。

5  基本经验

minor gc是运行在新生代的gc,major GC是运行在年老代的GC,比minor慢十倍。

大对象很有可能直接进入年老代,程序中尽量避免短命大对象(数组、列表)。

一次minor没被收集,从eden进入survivor,每过一次GC,survivor加一岁,直到N岁进入年老区。

根据GC的工作原理,我们可以通过一些技巧和方式,让GC运行更加有效率,更加符合应用程序的要求。一些关于程序设计的几点建议:

1)最基本的建议就是尽早释放无用对象的引用。大多数程序员在使用临时变量的时候,都是让引用变量在退出活动域(scope)后,自动设置为 null.我们在使用这种方式时候,必须特别注意一些复杂的对象图,例如数组,队列,树,图等,这些对象之间有相互引用关系较为复杂。对于这类对象,GC 回收它们一般效率较低。如果程序允许,尽早将不用的引用对象赋为null,这样可以加速GC的工作。

2)尽量少用finalize函数。finalize函数是Java提供给程序员一个释放对象或资源的机会。但是,它会加大GC的工作量,因此尽量少采用finalize方式回收资源。

3)如果需要使用经常使用的图片,可以使用soft应用类型。它可以尽可能将图片保存在内存中,供程序调用,而不引起OutOfMemory.

4)注意集合数据类型,包括数组,树,图,链表等数据结构,这些数据结构对GC来说,回收更为复杂。另外,注意一些全局的变量,以及一些静态变量。这些变量往往容易引起悬挂对象(dangling reference),造成内存浪费。

5)当程序有一定的等待时间,程序员可以手动执行System.gc(),通知GC运行,但是Java语言规范并不保证GC一定会执行。使用增量式GC可以缩短Java程序的暂停时间。

时间: 2024-08-01 10:46:34

java中JVM虚拟机内存模型详细说明的相关文章

【转】JVM虚拟机内存模型

转发声明: 本文原创作者:书呆子Rico 作者博客地址:http://blog.csdn.net/justloveyou_/ 摘要: 我们都知道,Java程序在执行前首先会被编译成字节码文件,然后再由Java虚拟机执行这些字节码文件从而使得Java程序得以执行.事实上,在程序执行过程中,内存的使用和管理一直是值得关注的问题.Java虚拟机在执行Java程序的过程中会把它所管理的内存划分为若干个不同的数据区域,这些数据区域都有各自的用途,以及创建和销毁的时间,并且它们可以分为两种类型:线程共享的方

JVM虚拟机内存模型以及GC机制

JAVA堆的描述如下: 内存由 Perm 和 Heap 组成. 其中 Heap = {Old + NEW = { Eden , from, to } } JVM内存模型中分两大块,一块是 NEW Generation, 另一块是Old Generation. 在New Generation中,有一个叫Eden的空间,主要是用来存放新生的对象,还有两个Survivor Spaces(from,to), 它们用来存放每次垃圾回收后存活下来的对象.在Old Generation中,主要存放应用程序中生

Java虚拟机内存模型及垃圾回收监控调优

Java虚拟机内存模型及垃圾回收监控调优 如果你想理解Java垃圾回收如果工作,那么理解JVM的内存模型就显的非常重要.今天我们就来看看JVM内存的各不同部分及如果监控和实现垃圾回收调优. JVM内存模型         正如你上图所看到的,JVM内存可以划分为不同的部分,广义上,JVM堆内存可以划分为两部分:年轻代和老年代(Young Generation and Old Generation) 年轻代(Young Generation) 年轻代用于存放由new所生成的对象.当年轻代空间满时,

Java的虚拟机内存模型

JVM是JavaVirtualMachine(Java虚拟机)的缩写,JVM是一种用于计算设备的规范,它是一个虚构出来的计算机,是通过在实际的计算机上仿真模拟各种计算机功能来实现的.Java语言的一个非常重要的特点就是与平台的无关性.而使用Java虚拟机是实现这一特点的关键.一般的高级语言如果要在不同的平台上运行,至少需要编译成不同的目标代码.而引入Java语言虚拟机后,Java语言在不同平台上运行时不需要重新编译.Java语言使用Java虚拟机屏蔽了与具体平台相关的信息,使得Java语言编译程

【java】JVM的内存区域划分

学过C语言的朋友都知道C编译器在划分内存区域的时候经常将管理的区域划分为数据段和代码段,数据段包括堆.栈以及静态数据区.那么在Java语言当中,内存又是如何划分的呢? 由于Java程序是交由JVM执行的,所以我们在谈Java内存区域划分的时候事实上是指JVM内存区域划分.在讨论JVM内存区域划分之前,先来看一下Java程序具体执行的过程: 如上图所示,首先Java源代码文件(.java后缀)会被Java编译器编译为字节码文件(.class后缀),然后由JVM中的类加载器加载各个类的字节码文件,加

设置TOMCAT的JVM虚拟机内存大小

你知道如何设置TOMCAT的JVM虚拟机内存大小吗,这里和大家分享一下,JAVA程序启动时JVM都会分配一个初始内存和最大内存给这个应用程序.这个初始内存和最大内存在一定程度都会影响程序的性能. 设置TOMCAT的JVM虚拟机内存大小 Tomcat本身不能直接在计算机上运行,需要依赖于硬件基础之上的操作系统和一个java虚拟机.JAVA程序启动时JVM都会分配一个初始内存 和最大内存给这个应用程序.这个初始内存和最大内存在一定程度都会影响程序的性能.比如说在应用程序用到最大内存的时候,JVM是要

JVM的内存模型

JVM的内存模型 概述 Java虚拟机在执行java程序的过程中,会把它所管理的内存划分为若干个不同的数据区域.这些区域都有各自的用途,以及创建和销毁的时间,有的区域随着虚拟机进程的启动而存在,有些区域则依赖用户线程的启动和结束而建立和销毁. java虚拟机所管理的内存包括以下几个运行时数据区域: 方法区(包括运行时常量池):存储类信息.常量.静态变量.即时编译器编译后的代码等:各种字面量和符号引用. Java堆:存储java对象. 虚拟机栈:存储方法以及方法中的存储局部变量表(存放基本数据类型

java中的常用内存区域总结

<开发实战经典>     (1)栈内存空间:保存所有的对象名称     (2)堆内存空间:保存每个对象的具体属性内容     (3)全局数据区:保存static类型的属性     (4)全局代码区:保存所有的方法定义 <--->     (1)栈区:存放局部变量     (2)堆区:存放对象属性,new出来的数据     (3)方法区:存储和class相关的信息     (4)本地方法区:系统相关,无需程序员管理     (5)寄存器:cpu相关,无需程序员管理 <深入理解j

java学习-----jvm的内存分配及运行机制

VM运行时数据区域: 根据<Java虚拟机规范(第二版)>的规定,JVM包括下列几个运行时区域: 我们思考几个问题: 1.jVM是怎么运行的? 2.JVM运行时内存是怎么分配的? 3.我们写的java代码(类,对象,方法,常量,变量等等)最终存放在哪个区? VM运行时数据区域: 1.程序计数器(program Counter Register):   是一块较小的内存空间,它的作用可以看做是当前线程所执行的字节码的行号指示器.在虚拟机的概念模型里(仅是概念模型,各种虚拟机可能会通过一些更高效的