JVM的内存管理机制Word文档下载推荐.docx
- 文档编号:20741103
- 上传时间:2023-01-25
- 格式:DOCX
- 页数:7
- 大小:118.70KB
JVM的内存管理机制Word文档下载推荐.docx
《JVM的内存管理机制Word文档下载推荐.docx》由会员分享,可在线阅读,更多相关《JVM的内存管理机制Word文档下载推荐.docx(7页珍藏版)》请在冰豆网上搜索。
2、MethodArea(方法区域):
被装载的class的信息存储在Methodarea的内存中。
当虚拟机装载某个类型时,它使用类装载器定位相应的class文件,然后读入这个class文件内容并把它传输到虚拟机中。
3、JavaStack(java的栈):
虚拟机只会直接对Javastack执行两种操作:
以帧为单位的压栈或出栈
4、ProgramCounter(程序计数器):
每一个线程都有它自己的PC寄存器,也是该线程启动时创建的。
PC寄存器的内容总是指向下一条将被执行指令的饿地址,这里的地址可以是一个本地指针,也可以是在方法区中相对应于该方法起始指令的偏移量。
5、Nativemethodstack(本地方法栈):
保存native方法进入区域的地址
以上五部分只有Heap和MethodArea是被所有线程的共享使用的;
而Javastack,Programcounter和Nativemethodstack是以线程为粒度的,每个线程独自拥有自己的部分。
了解JVM的系统结构,再来看看JVM内存回收问题了——
Sun的JVMGenerationalCollecting(垃圾回收)原理是这样的:
把对象分为年青代(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空间,而持久代则是之前提到的MethodArea,不属于Heap。
了解完这些之后,以下的转载一热衷于钻研技术的哥们RichenWang关于内存管理的一些建议——
1、手动将生成的无用对象,中间对象置为null,加快内存回收。
2、对象池技术如果生成的对象是可重用的对象,只是其中的属性不同时,可以考虑采用对象池来较少对象的生成。
如果有空闲的对象就从对象池中取出使用,没有再生成新的对象,大大提高了对象的复用率。
3、JVM调优通过配置JVM的参数来提高垃圾回收的速度,如果在没有出现内存泄露且上面两种办法都不能保证内存的回收时,可以考虑采用JVM调优的方式来解决,不过一定要经过实体机的长期测试,因为不同的参数可能引起不同的效果。
如-Xnoclassgc参数等。
推荐的两款内存检测工具
1、jconsoleJDK自带的内存监测工具,路径jdkbin目录下jconsole.exe,双击可运行。
连接方式有两种,第一种是本地方式如调试时运行的进程可以直接连,第二种是远程方式,可以连接以服务形式启动的进程。
远程连接方式是:
在目标进程的jvm启动参数中添加-Dcom.sun.management.jmxremote.port=1090-Dcom.sun.management.jmxremote.ssl=false-Dcom.sun.management.jmxremote.authenticate=false1090是监听的端口号具体使用时要进行修改,然后使用IP加端口号连接即可。
通过该工具可以监测到当时内存的大小,CPU的使用量以及类的加载,还提供了手动gc的功能。
优点是效率高,速度快,在不影响进行运行的情况下监测产品的运行。
缺点是无法看到类或者对象之类的具体信息。
使用方式很简单点击几下就可以知道功能如何了,确实有不明白之处可以上网查询文档。
2、JProfiler收费的工具,但是到处都有破解办法。
安装好以后按照配置调试的方式配置好一个本地的session即可运行。
可以监测当时的内存、CPU、线程等,能具体的列出内存的占用情况,还可以就某个类进行分析。
优点很多,缺点太影响速度,而且有的类可能无法被织入方法,例如我使用jprofiler时一直没有备份成功过,总会有一些类的错误
简单的概念:
堆(Heap)和非堆(Non-heap)内存
按照官方的说法:
“Java虚拟机具有一个堆,堆是运行时数据区域,所有类实例和数组的内存均从此处分配。
堆是在Java虚拟机启动时创建的。
”“在JVM中堆之外的内存称为非堆内存(Non-heapmemory)”。
可以看出JVM主要管理两种类型的内存:
堆和非堆。
简单来说堆就是Java代码可及的内存,是留给开发人员使用的;
非堆就是JVM留给自己用的,所以方法区、JVM内部处理或优化所需的内存(如JIT编译后的代码缓存)、每个类结构(如运行时常数池、字段和方法数据)以及方法和构造方法的代码都在非堆内存中。
堆内存分配
JVM初始分配的内存由-Xms指定,默认是物理内存的1/64;
JVM最大分配的内存由-Xmx指定,默认是物理内存的1/4。
默认空余堆内存小于40%时,JVM就会增大堆直到-Xmx的最大限制;
空余堆内存大于70%时,JVM会减少堆直到-Xms的最小限制。
因此服务器一般设置-Xms、-Xmx相等以避免在每次GC后调整堆的大小。
非堆内存分配
JVM使用-XX:
PermSize设置非堆内存初始值,默认是物理内存的1/64;
由XX:
MaxPermSize设置最大非堆内存的大小,默认是物理内存的1/4。
JVM内存限制(最大值)
首先JVM内存限制于实际的最大物理内存(废话!
呵呵),假设物理内存无限大的话,JVM内存的最大值跟操作系统有很大的关系。
简单的说就32位处理器虽然可控内存空间有4GB,但是具体的操作系统会给一个限制,这个限制一般是2GB-3GB(一般来说Windows系统下为1.5G-2G,Linux系统下为2G-3G),而64bit以上的处理器就不会有限制了
Java语言具备GC(垃圾回收)的能力,内存管理不需要应用程序去过问,这很方便。
但是,GC是怎么进行的,JVM的内存参数应该怎么调整,如何优化,往往我们不是太清楚。
看过一些资料后,对SunJVM的内存管理以及垃圾回收的机制大概有了一个概念,这里将这些资料归纳和翻译出来。
本文内容主要基于SunJVM1.3.1,在后续版本中有不少优化措施,但是这些基本概念还是不变的。
这里假设大家对GC的概念和基本原理都已经了解,不详细叙述了。
当JVM进行GC的时候,是要消耗CPU资源和需要一定时间的,这会影响到程序的正常运行,因此需要尽可能减少GC消耗的时间。
Java程序运行过程中,对象的生命周期有长有短,其中相当大部分是都是比较短命的,例如局部的对象一用完就可以回收了。
在大多数情况下,只要能够及时回收这些短命对象的内存,就能够确保JVM有足够内存来分配给新的对象。
因此JVM采用一种分代回收(generationalcollection)的策略,用较高的频率对年轻的对象(younggeneration)进行扫描和回收,这种叫做minorcollection,而对老对象(oldgeneration)的检查回收频率要低很多,称为majorcollection。
这样就不需要每次GC都将内存中所有对象都检查一遍。
SunJVM1.3有两种最基本的内存收集方式:
一种称为copying或scavenge,将所有仍然生存的对象搬到另外一块内存后,整块内存就可回收。
这种方法有效率,但需要有一定的空闲内存,拷贝也有开销。
这种方法用于minorcollection。
另外一种称为mark-compact,将活着的对象标记出来,然后搬迁到一起连成大块的内存,其他内存就可以回收了。
这种方法不需要占用额外的空间,但速度相对慢一些。
这种方法用于majorcollection.
在JVM1.3及以后的版本中,还有其他可选的内存收集方法,通过特定的参数来设定。
例如:
增量式回收,每次只处理一小部分;
替代单线程copying的多线程并行回收;
替代mark-compact的concurrentmark-sweep回收等等。
参考资料[4][5]中有更多描述。
JVM管理的内存,通常叫做堆(heap),可以用下面的图来描述。
JVM启动后,保留一段地址空间,这个空间的大小由-Xmx指定。
这块空间的大小就是heap可能的最大值,但一开始不一定全都分配了物理内存,初始分配的heap大小由-Xms指定,如果-Xms小于-Xmx,剩余部分是virtual的,当需要的时候,再向OS申请。
而且申请之后,是继续占用而不释放给该jvm以外的程序。
比如你的jvm申请了1G的内存,刚开始用了200M,然后随着程序的进行,内存用到900M,然后进行垃圾回收,想释放一些内存给其他程序,这是不可以的,此时,jvm依然会保有着900M内存。
内存的监控可以查看文章运用Jconsole监控JVM
绿色部分是younggeneration的内存,由一块Eden(伊甸园,有意思)和两块SurvivorSpace(1.4文档中称为semi-space)构成。
新创建的对象的内存都分配自eden。
两块SurvivorSpace总有会一块是空闲的,用作copyingcollection的目标空间。
Minorcollection的过程就是将eden和在用survivorspace中的活对象copy到空闲survivorspace中。
所谓survivor,也就是大部分对象在伊甸园出生后,根本活不过一次GC。
对象在younggeneration里经历了一定次数的minorcollection后,年纪大了,就会被移到oldgeneration中,称为tenuring。
(是否仅当survivorspace不足的时候才会将老对象tenuring?
目前资料中没有找到描述)
浅蓝色部分是oldgeneration的内存。
深蓝色部分称为permanentgeneration,是JVM用来保存classobject和metadata,大小由-XX:
PermSize和-XX:
MaxPermSize指定。
大量动态生成(编译)和加载class会增加这部分内存的耗用。
剩余内存空间不足会触发GC,如eden空间不够了就要进行minorcollection,oldgeneration空间不够要进行majorcollection,permanentgeneration空间不足会引发fullGC。
很多参数会影响里面各部分空间的分配。
-XX:
MinHeapFreeRatio与-XX:
MaxHeapFreeRatio设定空闲内存占总内存的比例范围,这两个参数会影响GC的频率和单次GC的耗时。
NewRatio决定young与oldgeneration的比例。
Younggeneration空间越大,minorcollection频率越低,但是oldgeneration空间小了,又可能导致majorcollection频率增加。
NewSize和-XX:
MaxNewSize直接指定了younggeneration的缺省大小和最大大小。
-Xmx
setmaximumJavaheapsize
-Xms
setinitialJavaheapsize
MinHeapFreeRatio=40
MinimumpercentageofheapfreeafterGCtoavoidexpansion.
MaxHeapFreeRatio=70
MaximumpercentageofheapfreeafterGCtoavoidshrinking.
NewRatio=2
Ratioofnew/oldgenerationsizes.[Sparc-client:
8;
x86-server:
x86-client:
12.]-client:
8(1.3.1+),x86:
12]
NewSize=2.125m
Defaultsizeofnewgeneration(inbytes)[5.0andnewer:
64bitVMsarescaled30%larger;
x86:
1m;
x86,5.0andolder:
640k]
MaxNewSize=
Maximumsizeofnewgeneration(inbytes).Since1.4,MaxNewSizeiscomputedasafunctionofNewRatio.
SurvivorRatio=25
Ratioofeden/survivorspacesize[Solarisamd64:
6;
Sparcin1.3.1:
25;
otherSolarisplatformsin5.0andearlier:
32]
PermSize=
Initialsizeofpermanentgeneration
MaxPermSize=64m
SizeofthePermanentGeneration.
[5.0andnewer:
1.4amd64:
96m;
1.3.1-client:
32m.]
(上面给出的缺省值不一定准确,不同JVM版本和不同OS环境下会有不同)
这里给出的只是基本的介绍,下面reference中的文章都很不错,对进一步了解或者查找性能优化参数都有帮助。
Reference
1.
2.
3.
4.
5.
6.
补习了一下jvm的内存管理知识,有以下心得分享:
1、jvm的内存分区分级大粒度管理相较memcache的固定单元小粒度内存管理,拥有更高的内存利用率,但带来内存碎片的问题。
2、为了解决内存碎片问题,jvm采取了碎片整理的方式,但碎片整理是很耗时的。
3、为了提高碎片整理的效率,因此引入了周期性的GC,而且分区分级的方式也控制了每次GC和碎片整理的范围。
4、由于jvm使用堆内存来存储局部变量,而局部变量具有生存周期短,先申请的后释放的特点,因此在低级别的分区中进行GC是效率最高的方式。
感觉环环相扣,有点奇妙。
再补充GC的一个作用:
寻找回路的孤立存储,并释放其占用的空间。
这更要求GC的非实时、周期性
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- JVM 内存 管理机制