java内存图解_java内存模型及GC原理 和 图解JVM在内存中申请对象及垃圾回收流程...

java内存模型

JVM内存模型中分两大块,一块是 NEW Generation,

另一块是Old Generation. 在New

Generation中,有一个叫Eden的空间,主要是用来存放新生的对象,还有两个Survivor Spaces(from,to),

它们用来存放每次垃圾回收后存活下来的对象。在Old

Generation中,主要存放应用程序中生命周期长的内存对象,还有个Permanent

Generation,主要用来放JVM自己的反射对象,比如类对象和方法对象等。

在New

Generation块中,垃圾回收一般用Copying的算法,速度快。每次GC的时候,存活下来的对象首先由Eden拷贝到某个Survivor

Space, 当Survivor Space空间满了后, 剩下的live对象就被直接拷贝到Old

Generation中去。因此,每次GC后,Eden内存块会被清空。在Old

Generation块中,垃圾回收一般用mark-compact的算法,速度慢些,但减少内存要求.

垃圾回收分多级,0级为全部(Full)的垃圾回收,会回收OLD段中的垃圾;1级或以上为部分垃圾回收,只会回收NEW中的垃圾,内存溢出通常发生于OLD段或Perm段垃圾回收后,仍然无内存空间容纳新的Java对象的情况。

1,out of

memory 只发生在jvm对old和perm generation

回收后还不能获足够内存的情况.

当一个URL被访问时,内存申请过程如下:

A. JVM会试图为相关Java对象在Eden中初始化一块内存区域

B. 当Eden空间足够时,内存申请结束。否则到下一步

C. JVM试图释放在Eden中所有不活跃的对象(这属于1或更高级的垃圾回收),

释放后若Eden空间仍然不足以放入新对象,则试图将部分Eden中活跃对象放入Survivor区

D.

Survivor区被用来作为Eden及OLD的中间交换区域,当OLD区空间足够时,Survivor区的对象会被移到Old区,否则会被保留在Survivor区

E. 当OLD区空间不够时,JVM会在OLD区进行完全的垃圾收集(0级)

F.

完全垃圾收集后,若Survivor及OLD区仍然无法存放从Eden复制过来的部分对象,导致JVM无法在Eden区为新对象创建内存区域,则出现”out

of memory错误”

造成full gc的原因:

new了很多对象,没有即时在主动释放掉->Eden内存不够用->不断把对象往old迁移->old满了->full

gc

full gc 如何预防,:

1,使用了缓存

访问有两种,第一种是缓存命中率不高的访问,第二种种是缓存命中率很高的访问.对于第一种情况,就没必要缓存了,缓存反而效果不好,浪费内存,没有提升程

序效率还浪费空间,特别是如果这种访问量级别很大的时候还会导致full

gc.第二种情况,不得不缓存很多对象,不缓存的话就要调用数据库或者其它是要发生io的,所以这时候要不就是想办法减少缓存对象的大小,例如不缓存没必

要缓存的数据,或者合并一些数据减少内存的使用.如果还是不行那就加机器,加内存.

总结:在不影响功能的情况下,缓存对象越小越要,命中率越高越好.低命中率的缓存对象还不如不缓存.

2,没使用缓存的情况,貌似不会出现full

gc的情况,除非内存太小,或者设置不对,程序有漏洞.

java内存管理以及GC

(强烈推荐)

目录 内存管理简介

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。关注点在内存空间利用率上。 a4c26d1e5885305701be709a3d33442f.png

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

代上)。 a4c26d1e5885305701be709a3d33442f.png

快速内存分配 大部分的内存分配请求发生时,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:+UseParallelOldGCConcurrent 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 -heap5695

Attaching to process ID5695, please wait...

Debugger attached successfully.

Server compiler detected.

JVM version is17.0-b16

using parallel threads in thenewgeneration.

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 +1Survivor 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

"font-size:16px;">$ jmap -heap5695

Attaching to process ID 5695, please wait...

Debugger attached successfully.

Server compiler detected.

JVM version is 17.0-b16

using parallel threads in the newgeneration.

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 + 1Survivor 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 -histo5695| less

num #instances #bytesclassname

----------------------------------------------

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

"font-size:16px;">$ jmap -histo5695| less

num #instances #bytes classname

----------------------------------------------

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文件。

先看一下JVM的内存模型:

a4c26d1e5885305701be709a3d33442f.png

从大的方面来讲,JVM的内存模型分为两大块:

永久区内存( Permanent

space)和堆内存(heap space)。

栈内存(stack

space)一般都不归在JVM内存模型中,因为栈内存属于线程级别。

每个线程都有个独立的栈内存空间。

Permanent

space里存放加载的Class类级对象如class本身,method,field等等。

heap space主要存放对象实例和数组。

heap

space由Old Generation和NewGeneration组成,OldGeneration存放生命周期长久的实例对象,而新的对象实例一般放在NewGeneration。

NewGeneration还可以再分为Eden区(圣经中的伊甸园)、和Survivor区,新的对象实例总是首先放在Eden区,Survivor区作为Eden区和Old区的缓冲,可以向Old区转移活动的对象实例。

下图是JVM在内存空间(堆空间)中申请新对象过程的活动图(点击看大图): a4c26d1e5885305701be709a3d33442f.png

没错,我们常见的OOM(out of

memory)内存溢出异常,就是堆内存空间不足以存放新对象实例时导致。

永久区内存溢出相对少见,一般是由于需要加载海量的Class数据,超过了非堆内存的容量导致。通常出现在Web应用刚刚启动时,因此Web应用推荐使用预加载机制,方便在部署时就发现并解决该问题。

栈内存也会溢出,但是更加少见。

堆内存优化:

调整JVM启动参数-Xms -Xmx  -XX:newSize -XX:MaxNewSize,如调整初始堆内存和最大对内存

-Xms256M -Xmx512M。

或者调整初始New Generation的初始内存和最大内存-XX:newSize=128M

-XX:MaxNewSize=128M。

永久区内存优化:

调整PermSize参数 如 -XX:PermSize=256M-XX:MaxPermSize=512M。

栈内存优化:

调整每个线程的栈内存容量 如 -Xss2048K

最终,一个运行中的JVM所占的内存=

堆内存 + 永久区内存 + 所有线程所占的栈内存总和 。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值