java heap 参数_JVM内存模型和常用参数配置

一、JVM内存模型

根据 JVM 规范,JVM 内存共分为虚拟机栈、堆、方法区、程序计数器、本地方法栈五个部分。

1.虚拟机栈(我们常说的栈、堆和方法区里面的栈stack)

每个线程有一个私有的栈,随着线程的创建而创建。栈里面存着的是一种叫“栈帧”的东西,每个方法会创建一个栈帧,栈帧中存放了局部变量表(基本数据类型和对象引用)、操作数栈、方法出口等信息。栈的大小可以固定也可以动态扩展。当栈调用深度大于JVM所允许的范围,会抛出StackOverflowError的错误,不过这个深度范围不是一个恒定的值。Java栈也称作虚拟机栈(Java Vitual Machine Stack),也就是我们常常所说的栈。JVM栈是线程私有的,每个线程创建的同时都会创建自己的JVM栈,互不干扰。

Java栈是Java方法执行的内存模型。Java栈中存放的是一个个的栈帧,每个栈帧对应一个被调用的方法,在栈帧中包括局部变量表(Local Variables)、操作数栈(Operand Stack)、指向当前方法所属的类的运行时常量池的引用(Reference to runtime constant pool)、方法返回地址(Return Address)和一些额外的附加信息。当线程执行一个方法时,就会随之创建一个对应的栈帧,并将建立的栈帧压栈。当方法执行完毕之后,便会将栈帧出栈。因此可知,线程当前执行的方法所对应的栈帧必定位于Java栈的顶部。

局部变量表:用来存储方法中的局部变量(包括在方法中声明的非静态变量以及函数形参)。对于基本数据类型的变量,则直接存储它的值,对于引用类型的变量,则存的是指向对象的引用。局部变量表的大小在编译期就可以确定其大小了,因此在程序执行期间局部变量表的大小是不会改变的。

操作数栈:栈最典型的一个应用就是用来对表达式求值。在一个线程执行方法的过程中,实际上就是不断执行语句的过程,而归根到底就是进行计算的过程。因此可以这么说,程序中的所有计算过程都是在借助于操作数栈来完成的。指向运行时常量池的引用:因为在方法执行的过程中有可能需要用到类中的常量,所以必须要有一个引用指向运行时常量。

方法返回地址:当一个方法执行完毕之后,要返回之前调用它的地方,因此在栈帧中必须保存一个方法返回地址。2.堆(heap)

自己画的示意图,不太规范,只能表示大概的意思(我理解的)

Java中的堆是用来存储对象实例以及数组(当然,数组引用是存放在Java栈中的)。堆是被所有线程共享的,因此在其上进行对象内存的分配均需要进行加锁,这也导致了new对象的开销是比较大的。在JVM中只有一个堆。堆是Java垃圾收集器管理的主要区域,Java的垃圾回收机制会自动进行处理。

Sun Hotspot JVM为了提升对象内存分配的效率,对于所创建的线程都会分配一块独立的空间TLAB(Thread Local Allocation Buffer),其大小由JVM根据运行的情况计算而得,在TLAB上分配对象时不需要加锁,因此JVM在给线程的对象分配内存时会尽量的在TLAB上分配,在这种情况下JVM中分配对象内存的性能和C基本是一样高效的,但如果对象过大的话则仍然是直接使用堆空间分配。

堆空间分为老年代和年轻代。刚创建的对象存放在年轻代,而老年代中存放生命周期长久的实例对象。年轻代中又被分为Eden区和两个Survivor区(From Space和To Space)。新的对象分配是首先放在Eden区,Survivor区作为Eden区和Old区的缓冲,在Survivor区的对象经历若干次GC仍然存活的,就会被转移到老年代。 当一个对象大于eden区而小于old区(老年代)的时候会直接扔到old区。 而当对象大于old区时,会直接抛出OutOfMemoryError(OOM)。3.方法区

在方法区中,存储了每个类的信息(包括类的名称、修饰符、方法信息、字段信息)、类中静态变量、类中定义为final类型的常量、类中的Field信息、类中的方法信息以及编译器编译后的代码等。程序中通过Class对象的getName、isInterface等方法来获取信息时,这些数据都来源于方法区域,同时方法区域也是全局共享的,在一定的条件下它也会被GC,在这里进行的GC主要是方法区里的常量池和类型的卸载。当方法区域需要使用的内存超过其允许的大小时,会抛出OutOfMemory的错误信息。jdk1.7之前:方法区位于永久代(Permanent Generation),永久代和堆相互隔离,永久代的大小在启动JVM时可以通过-XX:PermSize -XX:MaxPermSize设置初始空间和最大空间;

jdk.7:存储在永久代的部分数据就已经转移到Java Heap或者Native memory。但永久代仍存在于JDK 1.7中,并没有完全移除,譬如符号引用(Symbols)转移到了native memory;字符串常量池(interned strings)转移到了Java heap;类的静态变量(class statics variables )转移到了Java heap;

jdk1.8:仍然保留方法区的概念,只不过实现方式不同。取消永久代,方法存放于元空间(Metaspace),元空间仍然与堆不相连,但与堆共享物理内存,逻辑上可认为在堆中。移除了永久代(PermGen),替换为元空间(Metaspace);

永久代中的 class metadata 转移到了 native memory(本地内存,而不是虚拟机);

永久代中的 interned Strings 和 class static variables 转移到了 Java heap;

永久代参数 (PermSize MaxPermSize) -> 元空间参数(MetaspaceSize MaxMetaspaceSize)。

注意: Native memory:本地内存,也称为C-Heap,是供JVM自身进程使用的。当Java Heap空间不足时会触发GC,但Native memory空间不够却不会触发GC。)4.程序计数器(Program Counter Register

程序计数器(Program Counter Register),也有称作为PC寄存器。由于在JVM中,多线程是通过线程轮流切换来获得CPU执行时间的,因此,在任一具体时刻,一个CPU的内核只会执行一条线程中的指令,因此,为了能够使得每个线程都在线程切换后能够恢复在切换之前的程序执行位置,每个线程都需要有自己独立的程序计数器,并且不能互相被干扰,否则就会影响到程序的正常执行次序。因此,可以这么说,程序计数器是每个线程所私有的。

在JVM规范中规定,如果线程执行的是非native(本地)方法,则程序计数器中保存的是当前需要执行的指令的地址;如果线程执行的是native方法,则程序计数器中的值是undefined。

由于程序计数器中存储的数据所占空间的大小不会随程序的执行而发生改变,因此,对于程序计数器是不会发生内存溢出现象(OutOfMemory)的。5.本地方法栈

JVM采用本地方法堆栈来支持native方法的执行,此区域用于存储每个native方法调用的状态。本地方法栈与Java栈的作用和原理非常相似。区别只不过是Java栈是为执行Java方法服务的,而本地方法栈则是为执行本地方法(Native Method)服务的。在JVM规范中,并没有对本地方法栈的具体实现方法以及数据结构作强制规定,虚拟机可以自由实现它。在HotSopt虚拟机中直接就把本地方法栈和Java栈合二为一。

二、JVM常用参数配置打印GC详细信息-XX:+PrintGCDetails

Heap

PSYoungGen total 37888K, used 26384K [0x00000000d6180000, 0x00000000d8b80000, 0x0000000100000000)

eden space 32768K, 80% used [0x00000000d6180000,0x00000000d7b44178,0x00000000d8180000)

from space 5120K, 0% used [0x00000000d8680000,0x00000000d8680000,0x00000000d8b80000)

to space 5120K, 0% used [0x00000000d8180000,0x00000000d8180000,0x00000000d8680000)

ParOldGen total 86016K, used 0K [0x0000000082400000, 0x0000000087800000, 0x00000000d6180000)

object space 86016K, 0% used [0x0000000082400000,0x0000000082400000,0x0000000087800000)

Metaspace used 8410K, capacity 9382K, committed 9600K, reserved 1056768K

class space used 1104K, capacity 1303K, committed 1408K, reserved 1048576K

PSYoungGen Total = eden + from(to) = 32768 + 5120 = 37888K

[0x00000000d6180000(低边界), 0x00000000d8b80000(当前边界), 0x0000000100000000(最高边界))

(0x0000000100000000-0x00000000d6180000)/1024/1024=670.5M将gc日志导入到指定的位置-Xloggc:D:/log/gc.log

每次一次GC后,都打印堆信息-XX:+PrintHeapAtGC

Java HotSpot(TM) 64-Bit Server VM (25.131-b11) for windows-amd64 JRE (1.8.0_131-b11), built on Mar 15 2017 01:23:53 by "java_re" with MS VC++ 10.0 (VS2010)

Memory: 4k page, physical 8240240k(2285968k free), swap 12391624k(1821244k free)

CommandLine flags: -XX:InitialHeapSize=131843840 -XX:MaxHeapSize=2109501440 -XX:+PrintGC -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:+PrintHeapAtGC -XX:+UseCompressedClassPointers -XX:+UseCompressedOops -XX:-UseLargePagesIndividualAllocation -XX:+UseParallelGC

{Heap before GC invocations=1 (full 0):

PSYoungGen total 37888K, used 25063K [0x00000000d6180000, 0x00000000d8b80000, 0x0000000100000000)

eden space 32768K, 76% used [0x00000000d6180000,0x00000000d79f9f30,0x00000000d8180000)

from space 5120K, 0% used [0x00000000d8680000,0x00000000d8680000,0x00000000d8b80000)

to space 5120K, 0% used [0x00000000d8180000,0x00000000d8180000,0x00000000d8680000)

ParOldGen total 86016K, used 0K [0x0000000082400000, 0x0000000087800000, 0x00000000d6180000)

object space 86016K, 0% used [0x0000000082400000,0x0000000082400000,0x0000000087800000)

Metaspace used 8161K, capacity 9052K, committed 9216K, reserved 1056768K

class space used 1073K, capacity 1250K, committed 1280K, reserved 1048576K

0.460: [GC (Allocation Failure) [PSYoungGen: 25063K->3649K(37888K)] 25063K->3665K(123904K), 0.0556133 secs] [Times: user=0.00 sys=0.00, real=0.06 secs]

Heap after GC invocations=1 (full 0):

PSYoungGen total 37888K, used 3649K [0x00000000d6180000, 0x00000000d8b80000, 0x0000000100000000)

eden space 32768K, 0% used [0x00000000d6180000,0x00000000d6180000,0x00000000d8180000)

from space 5120K, 71% used [0x00000000d8180000,0x00000000d85104d0,0x00000000d8680000)

to space 5120K, 0% used [0x00000000d8680000,0x00000000d8680000,0x00000000d8b80000)

ParOldGen total 86016K, used 16K [0x0000000082400000, 0x0000000087800000, 0x00000000d6180000)

object space 86016K, 0% used [0x0000000082400000,0x0000000082404000,0x0000000087800000)

Metaspace used 8161K, capacity 9052K, committed 9216K, reserved 1056768K

class space used 1073K, capacity 1250K, committed 1280K, reserved 1048576K

}

{Heap before GC invocations=2 (full 0):4.监控类的加载-XX:+TraceClassLoading

[Loaded java.util.concurrent.ConcurrentHashMap$Traverser from D:\Program Files\Java\jdk1.8.0_131\jre\lib\rt.jar]

[Loaded java.util.concurrent.ConcurrentHashMap$BaseIterator from D:\Program Files\Java\jdk1.8.0_131\jre\lib\rt.jar]

[Loaded java.util.concurrent.ConcurrentHashMap$ValueIterator from D:\Program Files\Java\jdk1.8.0_131\jre\lib\rt.jar]

[Loaded org.junit.platform.engine.TestExecutionResult from file:/C:/Users/ASUS/.m2/repository/org/junit/platform/junit-platform-engine/1.5.2/junit-platform-engine-1.5.2.jar]

[Loaded org.junit.platform.engine.TestExecutionResult$Status from file:/C:/Users/ASUS/.m2/repository/org/junit/platform/junit-platform-engine/1.5.2/junit-platform-engine-1.5.2.jar]

[Loaded org.junit.jupiter.api.extension.TestWatcher from file:/C:/Users/ASUS/.m2/repository/org/junit/jupiter/junit-jupiter-api/5.5.2/junit-jupiter-api-5.5.2.jar]

[Loaded org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor$$Lambda$258/99451533 from org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor]

[Loaded org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor$$Lambda$259/84739718 from org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor]

...打印类的信息-XX:+PrintClassHistogram,需要按下Ctrl+Break后才会打印类的信息,否则不会起作用

num #instances #bytes class name

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

1: 890617 470266000 [B

2: 890643 21375432 java.util.HashMap$Node

3: 890608 14249728 java.lang.Long

4: 13 8389712 [Ljava.util.HashMap$Node;

5: 2062 371680 [C

6: 463 41904 java.lang.Class分配堆的大小-Xmx20m -Xms5m,-Xmx为最大堆空间、-Xms为最小堆空间,如果分配的堆太小,然后创建的对象大于堆的最大值,就会出现Exception in thread "main" java.lang.OutOfMemoryError: Java heap space的异常

//可以通过如下方式获取最大、空闲和总共

System.out.print("Xmx=");

System.out.println(Runtime.getRuntime().maxMemory()/1024.0/1024+"M");

System.out.print("free mem=");

System.out.println(Runtime.getRuntime().freeMemory()/1024.0/1024+"M");

System.out.print("total mem=");

System.out.println(Runtime.getRuntime().totalMemory()/1024.0/1024+"M");

//执行结果打印

Xmx=18.5M

free mem=3.26397705078125M

total mem=18.5M7.设置新生代的大小-Xmn

//-Xmn2m

PSYoungGen total 1536K, used 1008K [0x00000000ffe00000, 0x0000000100000000, 0x0000000100000000)

eden space 1024K, 98% used [0x00000000ffe00000,0x00000000ffefc158,0x00000000fff00000)

from space 512K, 0% used [0x00000000fff80000,0x00000000fff80000,0x0000000100000000)

to space 512K, 0% used [0x00000000fff00000,0x00000000fff00000,0x00000000fff80000)

PSYoungGen = eden space + from space + to space = 2M8.设置两个Survivor区和eden的比-XX:SurvivorRatio=2设置两个Survivor区和eden的比

8表示 两个Survivor :eden=2:8,即一个Survivor占年轻代的1/109.设置年轻代和老年代的比值-XX:NewRatio=4新生代(eden+2*s)和老年代(不包含永久区)的比值

4 表示 新生代:老年代=1:4,即年轻代占堆的1/510.OOM时导出堆到文件-XX:+HeapDumpOnOutOfMemoryError和指定OOM导出的地址-XX:HeapDumpPath

-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=D:log/test.dump

执行结果:

java.lang.OutOfMemoryError: Java heap space

Dumping heap to D:log/test.dump ...

Exception in thread "main" java.lang.OutOfMemoryError: Java heap space

at com.example.webserver.WebserverApplicationTests.contextLoads(WebserverApplicationTests.java:20)

at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)

at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)

at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)

at java.lang.reflect.Method.invoke(Method.java:498)

at org.junit.platform.commons.util.ReflectionUtils.invokeMethod(ReflectionUtils.java:675)

at org.junit.jupiter.engine.execution.MethodInvocation.proceed(MethodInvocation.java:60)

at org.junit.jupiter.engine.execution.InvocationInterceptorChain$ValidatingInvocation.proceed(InvocationInterceptorChain.java:125)

at org.junit.jupiter.engine.extension.TimeoutExtension.intercept(TimeoutExtension.java:132)

at org.junit.jupiter.engine.extension.TimeoutExtension.interceptTestableMethod(TimeoutExtension.java:124)

Heap dump file created [14489693 bytes in 0.074 secs]11.OOM时指定执行的脚本-XX:OnOutOfMemoryError在OOM时,执行一个脚本 "-XX:OnOutOfMemoryError=D:/tools/jdk1.7_40/bin/printstack.bat %p“

当程序OOM时,在D:/a.txt中将会生成线程的dump

可以在OOM时,发送邮件,甚至是重启程序12.设置永久区的初始大小和最大值-XX:PermSize -XX:MaxPermSize(jdk1.8之前的参数);1.8之后使用-XX:MetaspaceSize -XX:MaxMetaspaceSize

示例: -XX:MetaspaceSize=20m -XX:MaxMetaspaceSize=25m设置永久区(元空间)的初始空间和最大空间

他们表示,一个系统可以容纳多少个类型

超出的最大空间的时候会出现OutOfMemoryError: Metaspace

设置栈的大小-Xss通常只有几百K

决定了函数调用的深度

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

局部变量、参数 分配在栈上

超出就会报出java.lang.StackOverflowError异常

三、参数使用通过修改idea(eclipse)启动参数的设置如果是以jar包启动,只需要将启动参数附加在java -jar *.jar之后

以war包执行(Tomcat为例)打开catalin.bat或者catalin.sh

增加一行set "JAVA_OPTS=-Xss120k -XX:+PrintGCDetails -XX:+PrintHeapAtGC -Xloggc:D:/log/gc.log",通过JAVA_OPTS设置虚拟机参数

然后启动查看参数的配置效果

四、注意根据实际事情调整新生代和幸存代的大小

官方推荐新生代占堆的3/8

幸存代占新生代的1/10

在OOM时,记得Dump出堆,确保可以排查现场问题

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值