jvm的内存调优

运行时数据区域,所有类实例和数组的内存均从此处分配。Java 虚拟机启动时创建。对象的堆内存由称为垃圾回收器 的自动内存管理系统回收。

堆由两部分组成:

其中eden+fromspace+tospace也叫年轻代(young),old space叫旧生代.

其中还有S1,S0(在JDK的自带工具输出中会看到),分别指的是Survivor space,存放每次垃圾回收后存活的对象.

Old Generation , 主要存放应用程序中生命周期长的存活对象

垃圾回收主要是对Young Generation块和Old Generation块内存进行回收,YG用来放新产生的对象,经过几次回收还没回收掉的对象往OG中移动,

对YG进行垃圾回收又叫做MinorGC,对OG垃圾回收叫MajorGC,两块内存回收互不干涉

  1. 非堆内存

JVM具有一个由所有线程共享的方法区。方法区属于非堆内存。它存储每个类结构,如运行时常数池、字段和方法数据,以及方法和构造方法的代码。它是在 Java 虚拟机启动时创建的。

除了方法区外,Java 虚拟机实现可能需要用于内部处理或优化的内存,这种内存也是非堆内存。 例如,JIT 编译器需要内存来存储从 Java 虚拟机代码转换而来的本机代码,从而获得高性能。

Permanent Generation (图中的Permanent Space) 存放JVM自己的反射对象,比如类对象和方法对象

  1. 回收算法和过程

JVM采用一种分代回收 (generational collection) 的策略,用较高的频率对年轻的对象(young generation)进行扫描和回收,这种叫做minor collection,而对老对象(old generation)的检查回收频率要低很多,称为major collection。这样就不需要每次GC都将内存中所有对象都检查一遍。

当一个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错误"

对象衰老的过程

young generation的内存,由一块Eden(伊甸园,有意思)和两块Survivor Space(1.4文档中称为semi-space)构成。新创建的对象的内存都分配自eden。两块Survivor Space总有会一块是空闲的,用作copying collection的目标空间。Minor collection的过程就是将eden和在用survivor space中的活对象copy到空闲survivor space中。所谓survivor,也就是大部分对象在伊甸园出生后,根本活不过一次GC。对象在young generation里经历了一定次数的minor collection后,年纪大了,就会被移到old generation中,称为tenuring。(是否仅当survivor space不足的时候才会将老对象tenuring? 目前资料中没有找到描述)

剩余内存空间不足会触发GC,如eden空间不够了就要进行minor collection,old generation空间不够要进行major collection,permanent generation空间不足会引发full GC。

4 接下来这部分讲解的是TOMCAT或者其他服务器出现如下错误时的分析:

1、首先是:java.lang.OutOfMemoryError: Java heap space

解释:

Heap size 设置

JVM堆的设置是指java程序运行过程中JVM可以调配使用的内存空间的设置.JVM在启动的时候会自动设置Heap size的值,其初始空间(即-Xms)是物理内存的1/64,最大空间(-Xmx)是物理内存的1/4。可以利用JVM提供的-Xmn -Xms -Xmx等选项可进行设置。Heap size 的大小是Young Generation 和Tenured Generaion 之和。

提示:在JVM中如果98%的时间是用于GC且可用的Heap size 不足2%的时候将抛出此异常信息。

提示:Heap Size 最大不要超过可用物理内存的80%,一般的要将-Xms和-Xmx选项设置为相同,而-Xmn为1/4的-Xmx值。

解决方法:

手动设置Heap size

修改TOMCAT_HOME/bin/catalina.bat,在"echo "Using CATALINA_BASE: $CATALINA_BASE""上面加入以下行:

Java代码

set JAVA_OPTS=%JAVA_OPTS% -server -Xms800m -Xmx800m -XX:MaxNewSize=256m

set JAVA_OPTS=%JAVA_OPTS% -server -Xms800m -Xmx800m -XX:MaxNewSize=256m

或修改catalina.sh

在"echo "Using CATALINA_BASE: CATALINABASE""上面加入以下行:JAVAOPTS="������������""上面加入以下行:��������="JAVA_OPTS -server -Xms800m -Xmx800m -XX:MaxNewSize=256m"

2、其次是:java.lang.OutOfMemoryError: PermGen space

原因:

PermGen space的全称是Permanent Generation space,是指内存的永久保存区域,这块内存主要是被JVM存放Class和Meta信息的,Class在被Loader时就会被放到PermGen space中,它和存放类实例(Instance)的Heap区域不同,GC(Garbage Collection)不会在主程序运行期对PermGen space进行清理,所以如果你的应用中有很CLASS的话,就很可能出现PermGen space错误,这种错误常见在web服务器对JSP进行pre compile的时候。如果你的WEB APP下都用了大量的第三方jar, 其大小超过了jvm默认的大小(4M)那么就会产生此错误信息了。

解决方法:

  1. 手动设置MaxPermSize大小

修改TOMCAT_HOME/bin/catalina.bat(Linux下为catalina.sh),在Java代码

"echo "Using CATALINA_BASE: $CATALINA_BASE""上面加入以下行:

set JAVA_OPTS=%JAVA_OPTS% -server -XX:PermSize=128M -XX:MaxPermSize=512m

"echo "Using CATALINA_BASE: $CATALINA_BASE""上面加入以下行:

set JAVA_OPTS=%JAVA_OPTS% -server -XX:PermSize=128M -XX:MaxPermSize=512m

catalina.sh下为:

Java代码

JAVA_OPTS="$JAVA_OPTS -server -XX:PermSize=128M -XX:MaxPermSize=512m"

JAVA_OPTS="$JAVA_OPTS -server -XX:PermSize=128M -XX:MaxPermSize=512m"

JVM的默认设置

(heap)(News Generation 和Old Generaion 之和)的设置

初始分配的内存由-Xms指定,默认是物理内存的1/64但小于1G。

最大分配的内存由-Xmx指定,默认是物理内存的1/4但小于1G。

默认空余堆内存小于40%时,JVM就会增大堆直到-Xmx的最大限制,可以由-XX:MinHeapFreeRatio=指定。

默认空余堆内存大于70%时,JVM会减少堆直到-Xms的最小限制,可以由-XX:MaxHeapFreeRatio=指定。

服务器一般设置-Xms、-Xmx相等以避免在每次GC 后调整堆的大小,所以上面的两个参数没啥用。

-Xmn 设置young generation的heap大小

-XX:MinHeapFreeRatio与-XX:MaxHeapFreeRatio设定空闲内存占总内存的比例范围,这两个参数会影响GC的频率和单次GC的耗时。-XX:NewRatio决定young与old generation的比例。Young generation空间越大,minor collection频率越低,但是old generation空间小了,又可能导致major collection频率增加。-XX:NewSize和-XX:MaxNewSize直接指定了young generation的缺省大小和最大大小。

非堆内存的设置

默认分配为64M

-XX:PermSize设置最小分配空间,-XX:MaxPermSize设置最大分配空间。一般把这两个数值设为相同,以减少申请内存空间的时间。

再讲解和笔记下,JDK下的一些相关看内存管理工具的使用:

查看jvm内存状态:

jstat -gcutil pid 1000 20

异常情况的例子

jstat -gcutil pid 1000 20

S0 S1 E O P YGC YGCT FGC FGCT GCT

0.00 0.00 99.99 82.51 53.11 2409 1.205 10117 7250.393 7251.598

0.00 0.00 83.42 82.55 53.10 2409 1.205 10118 7252.650 7253.855

0.00 0.00 56.06 82.46 53.10 2410 1.205 10120 7254.467 7255.672

0.00 0.00 32.11 82.55 53.10 2411 1.205 10121 7256.673 7257.877

0.00 0.00 99.99 82.55 53.10 2412 1.205 10123 7257.026 7258.231

0.00 0.00 76.00 82.50 53.10 2412 1.205 10124 7259.241 7260.446

这个数据显示Full GC频繁发生。

正常情况的例子

S0 S1 E O P YGC YGCT FGC FGCT GCT

0.00 0.00 0.24 55.39 99.60 171 0.667 1339 393.364 394.031

0.00 0.00 0.24 55.39 99.60 171 0.667 1339 393.364 394.031

0.00 0.00 0.24 55.39 99.60 171 0.667 1339 393.364 394.031

0.00 0.00 0.24 55.39 99.60 171 0.667 1339 393.364 394.031

0.00 0.00 0.24 55.39 99.60 171 0.667 1339 393.364 394.031

0.00 0.00 0.24 55.39 99.60 171 0.667 1339 393.364 394.031

参数含义:

S0:Heap上的 Survivor space 0 段已使用空间的百分比

S1:Heap上的 Survivor space 1 段已使用空间的百分比

E: Heap上的 Eden space 段已使用空间的百分比

O: Heap上的 Old space 段已使用空间的百分比

P: Perm space 已使用空间的百分比

YGC:从程序启动到采样时发生Young GC的次数

YGCT:Young GC所用的时间(单位秒)

FGC:从程序启动到采样时发生Full GC的次数

FGCT:Full GC所用的时间(单位秒)

GCT:用于垃圾回收的总时间(单位秒)

2 Dump出内存

2.1 找出要dump的线程pid

在Linux下,使用ps --aux

2.2 Dump出内存使用详情

可以通过命令:

jmap -dump:file=a.hprof pid

例如:jmap -heap 2343,可以看到

Attaching to process ID 2343, please wait...

Debugger attached successfully.

Server compiler detected.

JVM version is 11.0-b16

using thread-local object allocation.

Parallel GC with 8 thread(s)

Heap Configuration:

MinHeapFreeRatio = 40

MaxHeapFreeRatio = 70

MaxHeapSize = 4294967296 (4096.0MB)

NewSize = 2686976 (2.5625MB)

MaxNewSize = -65536 (-0.0625MB)

OldSize = 5439488 (5.1875MB)

NewRatio = 2 (YG,OG 大小比为1:2)

SurvivorRatio = 8

PermSize = 21757952 (20.75MB)

MaxPermSize = 268435456 (256.0MB)

Heap Usage:

PS Young Generation

Eden Space:

capacity = 1260060672 (1201.6875MB)

used = 64868288 (61.86322021484375MB)

free = 1195192384 (1139.8242797851562MB)

5.148028935546367% used

From Space:

capacity = 85524480 (81.5625MB)

used = 59457648 (56.70323181152344MB)

free = 26066832 (24.859268188476562MB)

69.52120375359195% used

To Space:

capacity = 85852160 (81.875MB)

used = 0 (0.0MB)

free = 85852160 (81.875MB)

0.0% used

~~~~~~~~~~~~~~~~~~~~~~~~~~这三块为上面所说的YG大小和使用情况

PS Old Generation

capacity = 2291138560 (2185.0MB)

used = 1747845928 (1666.8757705688477MB)

free = 543292632 (518.1242294311523MB)

76.28722062099989% used

~~~~~~~~~~~~~~~~~~~~~~~~~~OG大小和使用情况

PS Perm Generation

capacity = 108265472 (103.25MB)

used = 107650712 (102.6637191772461MB)

free = 614760 (0.5862808227539062MB)

99.43217353728436% used

jstat

jstat是vm的状态监控工具,监控的内容有类加载、运行时编译及GC。

使用时,需加上查看进程的进程id,和所选参数。以下详细介绍各个参数的意义。

jstat -class pid:显示加载class的数量,及所占空间等信息。

jstat -compiler pid:显示VM实时编译的数量等信息。

jstat -gc pid:可以显示gc的信息,查看gc的次数,及时间。其中最后五项,分别是young gc的次数,young gc的时间,full gc的次数,full gc的时间,gc的总时间。

jstat -gccapacity:可以显示,VM内存中三代(young,old,perm)对象的使用和占用大小,如:PGCMN显示的是最小perm的内存使用量,PGCMX显示的是perm的内存最大使用量,PGC是当前新生成的perm内存占用量,PC是但前perm内存占用量。其他的可以根据这个类推, OC是old内纯的占用量。

jstat -gcnew pid:new对象的信息。

jstat -gcnewcapacity pid:new对象的信息及其占用量。

jstat -gcold pid:old对象的信息。

jstat -gcoldcapacity pid:old对象的信息及其占用量。

jstat -gcpermcapacity pid: perm对象的信息及其占用量。

jstat -util pid:统计gc信息统计。

jstat -printcompilation pid:当前VM执行的信息。

除了以上一个参数外,还可以同时加上 两个数字,如:jstat -printcompilation 3024 250 6是每250毫秒打印一次,一共打印6次,还可以加上-h3每三行显示一下标题。

例子:

jstat -gcutil pid 1000 20

S0 S1 E O P YGC YGCT FGC FGCT GCT

47.49 0.00 64.82 46.08 47.69 20822 2058.631 68 22.734 2081.365

0.00 37.91 38.57 46.13 47.69 20823 2058.691 68 22.734 2081.425 这里发生了一次YG GC,也就是MinorGC,耗时0.06s

46.69 0.00 15.19 46.18 47.69 20824 2058.776 68 22.734 2081.510

46.69 0.00 74.59 46.18 47.69 20824 2058.776 68 22.734 2081.510

0.00 40.29 19.95 46.24 47.69 20825 2058.848 68 22.734 2081.582

MajorGC平均时间:22.734/68=0.334秒

MinorGC平均时间:2058.691/20823=0.099秒

不健壮代码的特征及解决办法

1、尽早释放无用对象的引用。好的办法是使用临时变量的时候,让引用变量在退出活动域后,自动设置为null,暗示垃圾收集器来收集该对象,防止发生内存泄露。对于仍然有指针指向的实例,jvm就不会回收该资源,因为垃圾回收会将值为null的对象作为垃圾,提高GC回收机制效率;

2、我们的程序里不可避免大量使用字符串处理,避免使用String,应大量使用StringBuffer,每一个String对象都得独立占用内存一块区域;例如

3、尽量少用静态变量,因为静态变量是全局的,GC不会回收的;

4、避免集中创建对象尤其是大对象,JVM会突然需要大量内存,这时必然会触发GC优化系统内存环境;显示的声明数组空间,而且申请数量还极大。

5、尽量运用对象池技术以提高系统性能;生命周期长的对象拥有生命周期短的对象时容易引发内存泄漏,例如大集合对象拥有大数据量的业务对象的时候,可以考虑分块进行处理,然后解决一块释放一块的策略。

6、不要在经常调用的方法中创建对象,尤其是忌讳在循环中创建对象。可以适当的使用hashtable,vector 创建一组对象容器,然后从容器中去取那些对象,而不用每次new之后又丢弃

7、一般都是发生在开启大型文件或跟数据库一次拿了太多的数据,造成 Out Of Memory Error 的状况,这时就大概要计算一下数据量的最大值是多少,并且设定所需最小及最大的内存空间值。

相关推荐
王佑辉3 小时前
【jvm】如何判断一个对象是否可以回收
jvm
白总Server14 小时前
JVM解说
网络·jvm·物联网·安全·web安全·架构·数据库架构
向阳121814 小时前
JVM 进阶:深入理解与高级调优
java·jvm
用屁屁笑14 小时前
Java:JVM
java·开发语言·jvm
customer0818 小时前
【开源免费】基于SpringBoot+Vue.JS课程答疑系统(JAVA毕业设计)
java·jvm·vue.js·spring boot·spring cloud·kafka·开源
一叶飘零_sweeeet19 小时前
JVM 中的完整 GC 流程
java·开发语言·jvm
2301_7808538619 小时前
JVM概述
jvm
向阳121820 小时前
JVM入门教程:从概念到实践
java·jvm
不止会JS1 天前
JVM详解:JVM的系统架构
jvm·系统架构
学点东西吧.2 天前
JVM(一、基础知识)
java·jvm