前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Java堆内存设置

Java堆内存设置

作者头像
码客说
发布2020-05-09 14:59:28
3.1K0
发布2020-05-09 14:59:28
举报
文章被收录于专栏:码客码客

JVM内存区域

按照官方的说法:

Java 虚拟机具有一个堆,堆是运行时数据区域,所有类实例和数组的内存均从此处分配。堆是在 Java 虚拟机启动时创建的。

在JVM中堆之外的内存称为非堆内存(Non-heap memory)。

可以看出JVM主要管理两种类型的内存:堆和非堆。

简单来说堆就是Java代码可及的内存,是留给运行时使用的;非堆就是JVM留给自己用的, 所以方法区、JVM内部处理或优化所需的内存(如JIT编译后的代码缓存)、每个类结构(如运行时常数池、字段和方法数据)以及方法和构造方法的代码都在非堆内存中。

img
img

从上面的图可以看出, JVM区域总体分两类,heap区和非heap区。

heap区又分为:

  • Eden Space(伊甸园)
  • Survivor Space(幸存者区)
  • Old Gen(老年代)

非heap区又分:

  • Code Cache(代码缓存区)
  • Perm Gen(永久代)
  • Jvm Stack(java虚拟机栈)
  • Local Method Statck(本地方法栈)

堆分布

Java进程运行过程中创建的对象存放在堆中,堆被划分成两个不同的区域:新生代 ( Young )、老年代 ( Old )。

新生代 ( Young ) 又被划分为三个区域:Eden、From Survivor、To Survivor。

  • 新生代 (Eden空间,From Survivor空间,To Survivor空间)
  • 老年代

堆的内存模型大致为:

Java堆
Java堆

默认的:

代码语言:javascript
复制
NewRatio                 = 2  //老生代占2份 1:2
SurvivorRatio            = 8  //Eden占8份  也就是 Edem : from : to = 8 : 1 : 1

新生代 ( Young ) 与老年代 ( Old ) 的比例的值为 1:2 ( 该值可以通过参数 –XX:NewRatio 来指定 ),即:新生代 ( Young ) = 1/3 的堆空间大小。 老年代 ( Old ) = 2/3 的堆空间大小。

其中,新生代 ( Young ) 被细分为 Eden 和 两个 Survivor 区域,这两个 Survivor 区域分别被命名为 from 和 to,以示区分。 默认的,Edem : from : to = 8 : 1 : 1 ( 可以通过参数 –XX:SurvivorRatio 来设定 ),即: Eden = 8/10 的新生代空间大小,from = to = 1/10 的新生代空间大小。

JVM 每次只会使用 Eden 和其中的一块 Survivor 区域来为对象服务,所以无论什么时候,总是有一块 Survivor 区域是空闲着的。 因此,新生代实际可用的内存空间为 9/10 ( 即90% )的新生代空间。

新生代是 GC 收集垃圾的频繁区域。 当对象在 Eden ( 包括一个 Survivor 区域,这里假设是 from 区域 ) 出生后,在经过一次 Minor GC 后,如果对象还存活,并且能够被另外一块 Survivor 区域所容纳( 上面已经假设为 from 区域,这里应为 to 区域,即 to 区域有足够的内存空间来存储 Eden 和 from 区域中存活的对象 ),则使用复制算法将这些仍然还存活的对象复制到另外一块 Survivor 区域 ( 即 to 区域 ) 中,然后清理所使用过的 Eden 以及 Survivor 区域 ( 即 from 区域 ),并且将这些对象的年龄设置为1,以后对象在 Survivor 区每熬过一次 Minor GC,就将对象的年龄 + 1,当对象的年龄达到某个值时 ( 默认是 15 岁,可以通过参数 -XX:MaxTenuringThreshold 来设定 ),这些对象就会成为老年代。 但这也不是一定的,对于一些较大的对象 ( 即需要分配一块较大的连续内存空间 ) 则是直接进入到老年代。

From Survivor区域与To Survivor区域是交替切换空间,在同一时间内两者中会有一个为空。

常用命令

代码语言:javascript
复制
lsof -i:8902 ###查看端口8902对应应用的pid
jstat -class PID ###类加载统计
jstat -compiler PID ###编译统计
jstat -gc PID ###垃圾回收统计
jstat -gccapacity PID ### 堆内存统计
jstat -gcutil PID ###查看堆比例
jstat -gccause PID ###GC的原因
jstat -gcnew PID ###新生代垃圾回收统计
jstat -gcnewcapacity PID ###新生代内存统计
jstat -gcold PID ###老年代垃圾回收统计
jstat -gcoldcapcacity PID ###老年代内存统计
jstat -gcmetacapacity PID ###元数据空间统计
jstat -printcompilation PID ###JVM编译方法统计
jmap -histo PID ###查看类的实例
jmap -heap PID ###查看堆栈信息
jmap -dump:live,format=b,file=/tmp/m.hprof PID ###保存内存的堆栈为文件
jhat -J-Xmx2048m -port 5000 /tmp/m.hprof ###在线查看堆文件的类,速度比较慢
jcmd PID GC.run ###强制gc

查看内存占用

查看pid

代码语言:javascript
复制
lsof -i:8902

查看内存占用

代码语言:javascript
复制
jmap -heap pid

结果

Attaching to process ID 19438, please wait… Debugger attached successfully. Server compiler detected. JVM version is 25.221-b11 using thread-local object allocation. Parallel GC with 2 thread(s) Heap Configuration: MinHeapFreeRatio = 0 MaxHeapFreeRatio = 100 MaxHeapSize = 2147483648 (2048.0MB) NewSize = 89128960 (85.0MB) MaxNewSize = 715653120 (682.5MB) OldSize = 179306496 (171.0MB) NewRatio = 2 SurvivorRatio = 8 MetaspaceSize = 21807104 (20.796875MB) CompressedClassSpaceSize = 1073741824 (1024.0MB) MaxMetaspaceSize = 17592186044415 MB G1HeapRegionSize = 0 (0.0MB) Heap Usage: PS Young Generation Eden Space: capacity = 651689984 (621.5MB) used = 405279136 (386.5043029785156MB) free = 246410848 (234.99569702148438MB) 62.18894657739592% used From Space: capacity = 5242880 (5.0MB) used = 4774176 (4.553009033203125MB) free = 468704 (0.446990966796875MB) 91.0601806640625% used To Space: capacity = 14680064 (14.0MB) used = 0 (0.0MB) free = 14680064 (14.0MB) 0.0% used PS Old Generation capacity = 252182528 (240.5MB) used = 38105624 (36.340354919433594MB) free = 214076904 (204.1596450805664MB) 15.110334685835173% used 27080 interned Strings occupying 2811288 bytes.

查看某个进程的对象占用对象最大的命令:

代码语言:javascript
复制
jmap -histo pid | head -n 20

查看进程的可用内存占用百分比

代码语言:javascript
复制
jstat -gcutil pid

结果

S0

S1

E

O

M

CCS

YGC

YGCT

FGC

FGCT

GCT

88.75

0.00

90.80

17.78

91.23

87.83

82

0.805

3

0.492

1.297

  • S0: From Survivor
  • S1: To Survivor
  • E: Eden
  • O: Old generation
  • M: 的值为91.23,即Meta区使用率也达到了91.23%

配置优化

查看JDK版本

代码语言:javascript
复制
java -version

推荐配置(JDK8)

-XX:MetaspaceSize=128m (元空间默认大小) -XX:MaxMetaspaceSize=128m (元空间最大大小) -Xms1024m (jvm启动时分配的内存) -Xmx1024m (jvm运行过程中分配的最大内存) -Xmn256m (新生代大小) -Xss256k (jvm启动的每个线程分配的内存大小,默认JDK1.4中是256K,JDK1.5+中是1M) -XX:SurvivorRatio=8 (新生代分区比例 8:2 默认就是这个比例) -XX:+UseConcMarkSweepGC (指定使用的垃圾收集器,这里使用CMS收集器) -XX:+PrintGCDetails (打印详细的GC日志)

注意点:

JDK8之后把-XX:PermSize 和 -XX:MaxPermSize移除了,取而代之的是 -XX:MetaspaceSize=128m (元空间默认大小) -XX:MaxMetaspaceSize=128m (元空间最大大小) JDK 8开始把类的元数据放到本地化的堆内存(native heap)中,这一块区域就叫Metaspace,中文名叫元空间。 使用本地化的内存有什么好处呢?最直接的表现就是java.lang.OutOfMemoryError: PermGen 空间问题将不复存在,因为默认的类的元数据分配只受本地内存大小的限制,也就是说本地内存剩余多少,理论上Metaspace就可以有多大(貌似容量还与操作系统的虚拟内存有关?这里不太清楚),这解决了空间不足的问题。不过,让Metaspace变得无限大显然是不现实的,因此我们也要限制Metaspace的大小:使用-XX:MaxMetaspaceSize参数来指定Metaspace区域的大小。JVM默认在运行时根据需要动态地设置MaxMetaspaceSize的大小。

启动(JDK7及以下)

代码语言:javascript
复制
nohup java -Dfile.encoding=utf-8 -jar -Xms256m -Xmx2048m -XX:PermSize=128M -XX:MaxPermSize=256M /data/123.jar >/data/log.txt &

启动(JDK8及以上)

代码语言:javascript
复制
nohup java -Dfile.encoding=utf-8 -jar -Xms256m -Xmx2048m -XX:MetaspaceSize=256m -XX:MaxMetaspaceSize=256m /data/123.jar >/data/log.txt &

示例

代码语言:javascript
复制
java -Xmx3550m -Xms3550m -Xss128k -XX:NewRatio=4 -XX:SurvivorRatio=4 -XX:MaxPermSize=16m -XX:MaxTenuringThreshold=0
  • -XX:NewRatio=4 :设置新生代(包括Eden和两个Survivor区)与年老代的比值(除去持久代)。设置为4,则新生代与年老代所占比值为1:4,新生代占整个堆栈的1/5
  • -XX:SurvivorRatio=4 :设置新生代中Eden区与Survivor区的大小比值。设置为4,则两个Survivor区与一个Eden区的比值为2:4,一个Survivor区占整个新生代的1/6
  • -XX:MaxPermSize=16m :设置持久代大小为16m。
  • -XX:MaxTenuringThreshold=0 :设置垃圾最大年龄。如果设置为0的话,则新生代对象不经过Survivor区,直接进入年老代 。对于年老代比较多的应用,可以提高效率。如果将此值设置为一个较大值,则新生代对象会在Survivor区进行多次复制,这样可以增加对象再新生代的存活时间 ,增加在新生代即被回收的概论。

配置详解

  1. 堆设置
    • -Xms : 初始堆大小;
    • -Xmx : 最大堆大小;
    • -XX:MaxnewSize: 表示新生代可被分配的内存的最大上限;当然这个值应该小于 -Xmx的值;
    • -XX:NewRatio=3: 设置新生代和年老代的比值。 如:为3,表示新生代与年老代比值为1:3,新生代占整个新生代年老代和的1/4;
    • -XX:SurvivorRatio=3 : 新生代中Eden区与两个Survivor区的比值。注意Survivor区有两个。 如:3,表示Eden:Survivor=3:2,一个Survivor区占整个新生代的1/5;
    • -XX:NewSize=n : 设置新生代大小,应该小于 -Xms的值;
    • -Xmn256m: 至于这个参数则是对新生代 -XX:newSize-XX:MaxnewSize两个参数的同时配置, 也就是说如果通过-Xmn来配置新生代的内存大小,那么XX:newSize = XX:MaxnewSize = Xmn,虽然会很方便,但需要注意的是这个参数是在JDK1.4版本以后才使用的。
    • -Xss256k: jvm启动的每个线程分配的内存大小,默认JDK1.4中是256K,JDK1.5+中是1M
  2. 非堆设置 JDK7及以前
    • -XX:PermSize=128M 表示非堆区初始内存分配大小,其缩写为permanent size(持久化内存)
    • -XX:MaxPermSize=256M 表示非堆区最大内存分配大小

    JDK8及以后

    • -XX:MetaspaceSize=128m (元空间默认大小)
    • -XX:MaxMetaspaceSize=128m (元空间最大大小)

    建议

    1. MetaspaceSizeMaxMetaspaceSize设置一样大;
    2. 具体设置多大,建议稳定运行一段时间后通过jstat -gc pid确认且这个值大一些,对于大部分项目256m即可。
  3. 收集器设置
    • -XX:+UseSerialGC :设置串行收集器
    • -XX:+UseParallelGC :设置并行收集器
    • -XX:+UseParalledlOldGC :设置并行年老代收集器
    • -XX:+UseConcMarkSweepGC :设置并发收集器
  4. 垃圾回收统计信息
    • -XX:+PrintGC
    • -XX:+PrintGCDetails
    • -XX:+PrintGCTimeStamps
    • -Xloggc:filename
  5. 并行收集器设置
    • -XX:ParallelGCThreads=n :设置并行收集器收集时使用的CPU数。并行收集线程数。
    • -XX:MaxGCPauseMillis=n :设置并行收集最大暂停时间
    • -XX:GCTimeRatio=n :设置垃圾回收时间占程序运行时间的百分比。公式为1/(1+n)
  6. 并发收集器设置
    • -XX:+CMSIncrementalMode :设置为增量模式。适用于单CPU情况。
    • -XX:ParallelGCThreads=n :设置并发收集器新生代收集方式为并行收集时,使用的CPU数。并行收集线程数。
本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2020-03-23,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • JVM内存区域
  • 堆分布
  • 常用命令
  • 查看内存占用
  • 配置优化
  • 配置详解
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档