java内存管理以及GC
来源:互联网 发布:vmware12 mac显卡驱动 编辑:程序博客网 时间:2024/05/24 07:08
http://www.iteye.com/topic/976522
目录
内存管理简介
gc简介
好的collector的特性
设计或选择collector
gc性能指标
分代gc
java collector
快速内存分配
gc根集合
serial collector
parallel collector/throughput collector
parallel compacting collector
concurrent mark sweep collector (cms)
4种collector的对比和适用场景。
ergonomics
gc调优
outofmemoryerror
freememory(),totalmemory(),maxmemory()
jmap工具的使用
内存管理简介
内存管理的职责为分配内存,回收内存。
没有自动内存管理的语言/平台容易发生错误。
典型的问题包括悬挂指针问题,一个指针引用了一个已经被回收的内存地址,导致程序的运行完全不可知。
另一个典型问题为内存泄露,内存已经分配,但是已经没有了指向该内存的指针,导致内存泄露。
程序员要花费大量时间在调试该类问题上。
gc简介
因此引入了garbage collector机制,由运行时环境来自动管理内存。
garbage collector解决了悬挂指针和内存泄露大部分的问题(不是全部)。
注意garbage collector(简称collector)和garbage collection(简称gc)的区别。
collector的职责:
分配内存。
保证有引用的内存不被释放。
回收没有指针引用的内存。
对象被引用称为活对象,对象没有被引用称为垃圾对象/垃圾/垃圾内存,找到垃圾对象并回收是collector的一个主要工作,该过程称为gc。
collector一般使用一个称为堆的内存池来进行内存的分配和回收。
一般的,当堆内存满或者达到一个阀值时,堆内存或者部分堆内存被gc。
好的collector的特性
保证有引用的对象不被gc。
快速的回收内存垃圾。
在程序运行期间gc要高效,尽量少的影响程序运行。和大部分的计算机问题一样,这是一个关于空间,时间,效率平衡的问题。
避免内存碎片,内存碎片导致占用大量内存的大对象内存申请难以满足。可以采用compaction技术避免内存碎片。compaction技术:把活对象移向连续内存区的一端,回收其余的内存以便以后的分配。
良好的扩展性,内存分配和gc在多核机器上不应该成为性能瓶颈。
设计或选择collector
串行或并行。
串行collector在多核上也只有一个线程在运行,并行collector可以同时有多个线程执行gc,但是其算法更复杂。
并发或stop the world。
stop the world collection执行gc时,需要冻住所有内存,因此更简单一些,但是,在gc时,程序是被挂起的。并发gc时,程序和gc同时执行,当然,一般的并发gc算法还是需要一些stop the world时间。
compacting or non-compacting or copying
compacting: 去除内存碎片,回收内存慢,分配内存快。
non-compacting: 容易产生内存碎片,回收内存快,分配内存慢,对大对象内存分配支持不好。
copying: 复制活对象到新的内存区域,原有内存直接回收,需要额外的时间来做复制,额外的空间来做存储。
gc性能指标
throughput: 程序时间(不包含gc时间)/总时间。
gc overhead: gc时间/总时间。
pause time: gc运行时程序挂起时间。
frequency of gc: gc频率。
footprint: a measure of size, such as heap size。
promptness:对象变为垃圾到该垃圾被回收后内存可用的时间。
依赖于不同的场景,对于gc的性能指标的关注点也不一样。
分代gc
分代gc把内存划分为多个代(内存区域),每个代存储不同年龄的对象。 常见的分为2代,young和old。
分配内存时,先从young代分配,如果young代已满,可以执行gc(可能导致对象提升),如果有空间,则分配,如果young代还是没有空间,可以对整个内存堆gc。
young代gc后还存活的对象可以提升到old代。
该机制基于以下观察事实:
1 大部分新分配的对象很快就没有引用了,变成垃圾。
2 很少有old代对象引用young代对象。
基于代内存存储对象的特性,对不同代的内存可以使用不同的gc算法。
young代gc需要高效,快速,频繁的执行,关注点主要在速度上。
old代由于增长缓慢,因此gc不频繁,但是其内存空间比较大,因此,需要更长时间才能执行完gc。关注点在内存空间利用率上。
java collector
jvm的内存分为3代。young, old, permanent。
大部分对象存储在young代。
在young代中经历数次gc存活的对象可以提升到old代,大对象也可以直接分配到old代。
permanent代保存虚拟机自己的静态(refective)数据,例如类(class)和方法(method)对象。
young代由一个eden和2个survivor组成。大部分的对象的内存分配和回收在这里完成。
survivor存储至少经过一次gc存活下来的对象,以增大该对象在提升至old代前被回收的机会。2个survivor中有一个为空。分别为from和to survivor。
当young代内存满,执行young代gc(minor gc)。
当old或permanent代内存满,执行full gc(major gc),所有代都被gc。一般先执行young gc,再执行old, permanent gc。
有时old代太满,以至于如果young gc先运行,则无法存储提升的对象。这时,young gc不运行,old gc算法在整个堆上运行(cms collector是个例外,该collector不能运行在young 代上)。
快速内存分配
大部分的内存分配请求发生时,collector都有一块大的连续内存块,简单的内存大小计算和指针移动就可以分配内存了。因此非常快速。该技术称为bump –the-pointer技术。
对于多线程的内存分配,每个线程使用thread local allocation buffer(tlab)进行分配,因此还是很高效。tlab可以看作一个线程的特殊代。只有tlab满的时候才需要进行同步操作。
gc根集合
gc运行时当前程序可以直接访问的对象。如线程中当前调用栈的方法参数,局部变量,静态变量,当前线程对象等等。
collector根据gc根集合来寻找所有活对象。gc根集合不可达对象自然就是垃圾了。
serial collector
单线程,young and old gc是串行,stop the world gc的。
young gc。
eden中活对象copy到to survivor中,大对象直接进old代。
from survivor中相对老的活对象进入old代,相对年轻的对象进入to survivor中。
如果to survivor放不下活对象,则这些活对象直接进入old。
经历过young gc,eden和from survivor都变成空的内存区域,to survivor存储有活的对象。to survivor和from survivor角色互换。
old permanent gc。
mark-sweep-compact算法。
s1 标识哪些对象是活的对象。
s2 标识哪些对象是垃圾。
s3 把活的对象压缩到内存的一端,以便可以使用bump –the-pointer处理以后的内存分配请求。
非server-class machine 的默认gc。
也可以使用命令行参数来设定。
-xx:+useserialgc
parallel collector/throughput collector
利用了现代计算机大部分都是多核的事实。
young gc。
和serial collector一样,是一个stop the world和copying collector。只不过是多线程并行扫描和做copy,提高速度,减少了stop the world的时间,增大了throughput。
old permanent gc。
和serial collector一样。mark-sweep-compact算法。单线程。
server-class machine的默认gc。
也可以使用命令行参数来设定。
-xx:+useparallelgc
parallel compacting collector
young gc。
和parallel collector一样。
old permanent gc。
stop the world,并且多线程并发gc。
每一代被划分为一些长度固定的区域。
第1步(mark phase),gc根集合划分后分发给多个gc线程,每个gc线程更新可达活对象所在区域的信息(活对象的内存位置,大小)。
第2步(summary phase),操作在区域上,而不是对象上。由于以前gc的影响,内存的一端活对象的密度比较高,在该阶段找到一个临界点,该临界点以前的区域由于活对象内存密度高,不参与gc,不做compact。该临界点之后的区域参与gc,做compact。该阶段为单线程执行。
第3步(compact phase)。gc多线程使用summary info做回收和compact工作。
可以设置gc线程数,防止gc线程长时间占有整台机器的资源。
-xx:parallelgcthreads=n
使用命令行参数来设定。
-xx:+useparalleloldgc
concurrent mark sweep collector (cms)
young gc。
和parallel collector一样。
old permanent gc。
gc和程序并发执行。
initial phase:短暂停,标记gc根集合。单线程执行。
concurrent marking phase: gc多线程标记从根集合可达的所有活对象。程序和gc并发运行。由于是并发运行,有可能有活对象没有被标记上。
concurrent pre-clean:单线程,并发执行。
remark phase: 短暂停,多线程标记在concurrent marking phase中有变化的相关对象。
concurrent sweep phase:和程序并发执行。单线程执行。不做compacting。
concurrent reset:单线程,并发执行。
cms不做compacting,不能使用bump-the-pointer技术,只能使用传统的内存空闲链表技术。
导致内存分配变慢,影响了young代的gc速度,因为young的gc如果有对象提升的话依赖于old的内存分配。
cms需要更多的内存空间,因为mark phase时程序还是在运行,程序可以申请更多的old空间。在mark phase中,cms保证标识活对象,但是该过程中,活对象可能转变为垃圾,只能等待下一次gc才能回收。
和其他collector不同,cms不是等到old满时才gc,基于以前的统计数据(gc时间,old空间消耗速度)来决定何时gc。cms gc也可以基于old空间的占用率。
命令行参数:
-xx:cmsinitiatingoccupancyfraction=n,n为百分比,默认68。
可以设置
-xx:+usecmsinitiatingoccupancyonly 来使vm只使用old内存占用比来触发cms gc。
incremental mode。
cms的concurrent phase可以是渐进式执行。以减少程序的一次暂停时间。
命令行参数:
-xx:+useconcmarksweepgc
-xx:+cmsincrementalmode
4种collector的对比和适用场景。
直到jdk1.3.1,java只提供serial collector,serial collector在多核的机器上表现比较差。主要是throughput比较差。
大型应用(大内存,多核)应该选用并行collector。
serial collector:大多数client-style机器。对于低程序暂停时间没有需求的程序。
parallel collector:多核机器,对于低程序暂停时间没有需求的程序。
parallel compacting collector:多核机器,对于低程序暂停时间有需求的程序。
cms collector:和parallel compacting collector相比,降低了程序暂停时间,但是young gc程序暂停时间变长,需要更大的堆空间,降低了程序的throughput。
ergonomics
j2se 5.0后,collector的选择,堆大小的选择,vm(client还是server)的选择,都可以依赖平台和os来做自动选择。
jvm会自动选择使用server mode还是client mode。但是我们一样可以手工设置。
java -server -client
server-class machine的选择:
2个或更多的处理器
and
2g或更多的物理内存
and
不是32bits,windows os。
client-class
the client jvm
the serial collector
initial heap size = 4m
max heap size=64m
server-class
the server jvm
the parallel collector
initial heap size= 1/64物理内存(>=32m),最大1g。
max heap size=1/4物理内存,最大1g。
基于行为的调优。
可以基于最大暂停时间或throughput。
-xx:maxgcpausemillis=n
指示vm调整堆大小和其他参数来满足这个时间需求。如果vm不满足该目标,则减小堆大小来满足该目标。该目标没有默认值。
-xx:gctimeratio=n
gc time/app time=1/(1+n)
如n=99表示gc时间占整个运行时间的1%。
如果该目标不能满足,则增大堆大小来满足该目标。默认值n=99。
footprint goal
如果最大暂停时间和throughput目标都满足了,则减少堆大小直到有一个目标不满足,然后又回调。
目标优先级:
最大暂停时间>throughput>footprint。
gc调优
由于有了ergonomics,第一个建议就是不要手工去配置各种参数。让系统自己去根据平台和os来选择。然后观测性能,如果ok的话,呵呵,不用搞了。
但是ergonomics也不是万能的。因此还是需要程序员来手工搞。
注意性能问题一定要测量/调优/测量/调优不停的循环下去。
vm mode 选择。
java -server server mode.
java -client client mode.
观测性能主要使用gc的统计信息。
-xx:+printgc 输出gc信息。
-xx:+printgcdetails输出gc详细信息。
-xx:+printgctimestamps 输出时间戳,和–xx:+printgc 或–xx:+printgcdetails一起使用。
-xloggc: gc.log 输出到指定文件。
1 决定堆内存大小。
决定整个堆内存的大小。内存的大小对于collector的性能影响是最大的。
使用以下参数来决定堆内存的大小。
可以决定堆空间的起始值和最大值,大型程序可以考虑把起始值调大,避免程序启动时频繁gc和内存扩展申请。
以及堆空间中可用内存的比例范围,vm会动态管理堆内存来满足该比例范围。
-xx:minheapfreeratio=n
-xx:maxheapfreeratio=n
-xmsn young和old的起始内存
-xmxn young和old的最大内存
2 决定代空间大小。
young代空间越大,则minor gc的频率越小。但是,给定堆内存大小,young代空间大,则major gc频率变大。
如果没有过多的full gc或者过长的暂停时间问题,给young代尽量大的空间。
young generation guarantee。
对于serial collector,当执行minor gc时,必须保证old代中有可用的空间来处理最坏情况(即eden和survivor空间中的对象都是活对象,需要提升至old空间),如果不满足,则该minor gc触发major gc。所以对于serial collector,设置eden+survivor的内存不要大过old代内存。
其他collector不做该保证,只有old代无法存储提升对象时才触发major gc。
对于其他collector,由于多线程做minor gc时,考虑到最坏情况,每个线程要在old代内存预留一定空间做对象提升,因此可能导致内存碎片。因此old代内存应该调整的更大一些。
-xx:newsize=n young代空间下限。
-xx:maxnewsize=n young代空间上限。
-xx:newratio=n young和old代的比例。
-xx:survivorratio=n eden和单个survivor的比例。
-xx:permsize=n permanent起始值。
-xx:maxpermsize=n permanent最大值。
3 决定使用collector
可以考虑是否需要换一个collector。
collector选择
-xx:+useserialgc serial
-xx:+useparallelgc parallel
-xx:+useparalleloldgc parallel compacting
-xx:+useconcmarksweepgc concurrent mark–sweep (cms)
parallel和parallel compacting collector.
-xx:parallelgcthreads=n
-xx:maxgcpausemillis=n
-xx:gctimeratio=n
设定目标好于明确设定参数值。
为了增大throughput,堆大小需要变大。可以把堆大小设为物理内存允许的最大值(同时程序不swapping)来检测该环境可以支持的最大throughput。
为了减小最大暂停时间和footprint,堆大小需要变小。
2个目标有一定的矛盾,因此要视具体应用场景,做平衡。
cms collector
-xx:+cmsincrementalmode 和cms同时使用。
-xx:+cmsincrementalpacing 和cms同时使用。
-xx:parallelgcthreads=n
-xx:cmsinitiatingoccupancyfraction=n,n为百分比,默认68。
outofmemoryerror
可以指定
-xx:+heapdumponoutofmemoryerror
当发生outofmemoryerror时dump出堆内存。
发生outofmemoryerror时可以观测该error的详细信息。
java heap space:
调整堆大小。
程序中含有大量带有finalize方法的对象。执行finalize方法的线程顶不住了。
permgen space:
permanent代内存不够用了。
requested array size exceeds vm limit。
堆内存不够用。
程序bug,一次分配太多内存。
freememory(),totalmemory(),maxmemory()
java.lang.runtime类中的 freememory(), totalmemory(), maxmemory()这几个方法的反映的都是 java这个进程的内存情况,跟操作系统的内存根本没有关系。
maxmemory()这个方法返回的是java虚拟机(这个进程)能构从操作系统那里挖到的最大的内存,以字节为单位,如果在运行java程序的时 候,没有添加-xmx参数,那么就是jvm默认的可以使用内存大小,client为64m,server为1g。如果添加了-xmx参数,将以这个参数后面的值为准。
totalmemory()这个方法返回的是java虚拟机现在已经从操作系统那里挖过来的内存大小,也就是java虚拟机这个进程当时所占用的所有内存。如果在运行java的时候没有添加-xms参数,那么,在java程序运行的过程的,内存总是慢慢的从操作系统那里挖的,基本上是用多少挖多少,直 到挖到maxmemory()为止,所以totalmemory()是慢慢增大的。如果用了-xms参数,程序在启动的时候就会无条件的从操作系统中挖 -xms后面定义的内存数,然后在这些内存用的差不多的时候,再去挖。
freememory()是什么呢,刚才讲到如果在运行java的时候没有添加-xms参数,那么,在java程序运行的过程的,内存总是慢慢的从操 作系统那里挖的,基本上是用多少挖多少,但是java虚拟机100%的情况下是会稍微多挖一点的,这些挖过来而又没有用上的内存,实际上就是 freememory(),所以freememory()的值一般情况下都是很小的,但是如果你在运行java程序的时候使用了-xms,这个时候因为程 序在启动的时候就会无条件的从操作系统中挖-xms后面定义的内存数,这个时候,挖过来的内存可能大部分没用上,所以这个时候freememory()可能会有些大。
jmap工具的使用
jmap pid 查看共享对象。
jmap -heap pid 查看java进程堆的相关信息。
java代码
$ jmap -heap 5695
attaching to process id 5695, please wait...
debugger attached successfully.
server compiler detected.
jvm version is 17.0-b16
using parallel threads in the new generation.
using thread-local object allocation.
concurrent mark-sweep gc
heap configuration:
minheapfreeratio = 40
maxheapfreeratio = 70
maxheapsize= 1342177280 (1280.0mb)
newsize= 134217728 (128.0mb)
maxnewsize= 134217728 (128.0mb)
oldsize= 4194304 (4.0mb)
newratio= 2
survivorratio= 20000
permsize= 100663296 (96.0mb)
maxpermsize= 134217728 (128.0mb)
heap usage:
new generation (eden + 1 survivor space):
capacity = 134152192 (127.9375mb)
used= 34518744 (32.919639587402344mb)
free= 99633448 (95.01786041259766mb)
25.731032408326207% used
eden space:
capacity = 134086656 (127.875mb)
used= 34518744 (32.919639587402344mb)
free= 99567912 (94.95536041259766mb)
25.743608670500368% used
from space:
capacity = 65536 (0.0625mb)
used= 0 (0.0mb)
free= 65536 (0.0625mb)
0.0% used
to space:
capacity = 65536 (0.0625mb)
used= 0 (0.0mb)
free= 65536 (0.0625mb)
0.0% used
concurrent mark-sweep generation:
capacity = 671088640 (640.0mb)
used= 287118912 (273.81793212890625mb)
free= 383969728 (366.18206787109375mb)
42.7840518951416% used
perm generation:
capacity = 100663296 (96.0mb)
used= 41864504 (39.92510223388672mb)
free= 58798792 (56.07489776611328mb)
41.58864816029867% used
jmap –histo pid 查询各种对象占用的内存大小。
java代码
$ jmap -histo 5695 | less
num#instances#bytesclass name
----------------------------------------------
1:32029063305456[c
2:145701046624320java.util.concurrent.concurrenthashmap$segment
3:150250036060000java.util.concurrent.locks.reentrantlock$nonfairsync
4:8778529987632[i
5:145701023638928[ljava.util.concurrent.concurrenthashmap$hashentry;
6:28566815240784[ljava.lang.object;
7:8723910680160
8:3994829587568java.lang.string
9:165337466624[b
10:910657285072[ljava.util.concurrent.concurrenthashmap$segment;
11:872396983288
12:1257505868720
13:454095449080java.net.sockssocketimpl
14:635744936176[s
15:452944710576sun.nio.ch.socketchannelimpl
jmap –permstat pid 查看class loader。
jmap –dump:file=filename,format=b pid dump内存到文件。
可以使用mat工具分析java dump文件。
======================================================
在最后,我邀请大家参加新浪APP,就是新浪免费送大家的一个空间,支持PHP+MySql,免费二级域名,免费域名绑定 这个是我邀请的地址,您通过这个链接注册即为我的好友,并获赠云豆500个,价值5元哦!短网址是http://t.cn/SXOiLh我创建的小站每天访客已经达到2000+了,每天挂广告赚50+元哦,呵呵,饭钱不愁了,\(^o^)/
- java内存管理以及GC
- java内存管理以及GC工作原理
- JVM基础 之java内存管理以及GC
- JAVA 内存管理及GC
- java内存管理,GC,内存泄漏
- Java的GC以及内存泄漏
- java内存模型以及GC原理
- Java 堆内存分析以及gc
- Java内存模型以及gc算法
- Java 垃圾回收机制 [ 内存管理 GC]
- Java的GC与内存管理
- JAVA内存管理和GC机制
- JAVA内存管理与GC机制
- 内存管理和GC算法以及回收策略
- 内存管理之GC
- java学习(5),JVM内存模型以及GC回收算法
- sun培训笔记--java内存管理,GC调优
- Java虚拟机_内存管理_垃圾收集GC
- 我知道的JavaScript--Json对象扁平化
- 关于Java AOP和dotnet attribute
- 让客户端看不见你的Javascript脚本.
- java的初始化顺序
- JAVA Functions in XI(转)
- java内存管理以及GC
- 解决java和.NET互相操作memcache差异问题
- java的内部类与匿名类
- Android中String资源文件的String.format方法(java)
- Java脚本-BeanShell
- javascript中parseInt的问题
- Java初体验
- java keytool 用法
- Java编程之四大名著