一:jvm的内存简化架构
二:运行时数据区
Java虚拟机定义了若干种程序运行期间会使用到的运行时数据区,其中有一些会随着虚拟机启动而创建,随着虚拟机的退出而销毁。另外一些则是与线程一一对应的,这些与线程对应的数据区域会随着线程的开始和结束而创建和销毁。其分为pc寄存器(程序计数器)、java虚拟机栈、Java堆、方法区、运行常量池、本地方法栈
- PC(Program Counter)寄存器:
a.Java虚拟机可以支持多条线程同时执行,每一条Java虚拟机线程都有自己的pc寄存器。
b.在任意时刻,一条java虚拟机线程只会执行一个方法的代码,这个正在被线程执行的方法称为该线程的当前方法。
c.如果当前方法不是native的,pc寄存器保存java虚拟机正在执行的字节码指令的地址。
d.如果当前方法是native的,pc寄存器的值是undefined - Java虚拟机栈:
a.栈由一系列帧(Frame)组成(因此Java栈也叫做帧栈),是线程私有的
b.帧用来保存一个方法的局部变量、操作数栈( Java没有寄存器,所有参数传递使用操作数栈)、常量池
指针、动态链接、方法返回值等
c.每一次方法调用创建一个帧,并压栈,退出方法的时候,修改栈顶指针就可以把栈帧中的内容销毁
d.局部变量表存放了编译期可知的各种基本数据类型和引用类型,每个slot存放32位的数据,long、
double占两个槽位
e.栈的优点:存取速度比堆快,仅次于寄存器
e.栈的缺点:存在栈中的数据大小、生存期是在编译期决定的,缺乏灵活性
- Java堆:
a.用来存放应用系统创建的对象和数组,所有线程共享Java堆
b.GC主要就管理堆空间,对分代GC来说,堆也是分代的
c.堆的优点:运行期动态分配内存大小,自动进行垃圾回收
d.堆的缺点:效率相对较慢 - 方法区:
a.方法区是线程共享的,通常用来保存装载的类的结构信息,比如:
运行时常量池字段,方法数据、方法字节码在类、实例、接口初始化用到的特殊方法
b.通常和永久区关联在一起,但具体的跟JVM的实现和版本有关,比如JDK7就把String从方法区移到堆了,JDK8直接去掉 永久区,取而代之的是元空间。
c.JVM规范把方法区描述为堆的一个逻辑部分,但它有一个别名称为Non-heap(非堆),应该是为了与Java堆区分开 - 运行常量池:
a.是Class文件中每个类或接口的常量池表在运行期间的表示形式,通常段、方法、接口等信息
b.在方法区中分配
c.通常在加载类和接口到JVM后,就创建相应的运行时常量池 - 本地方法栈
在JVM中用来支持native方法执行的栈就是本地方法栈。
三:堆的结构
1:整个堆大小 = 新生代+老年代
2:新生代 = 1个eden区 + 2个survivor区,新生代用来放刚分配的对象
3:老年代=整个堆-新生代,新生中经过垃圾回收, 没有回收掉的对象,被复制到老年代。老
年代存储对象比新生年龄大得多,包含一些大对象
4:从前的持久代,用来存放Class、Method等元信息的区域,在JDK8中已经没有了,取而代
之的是元空间(MetaSpace),元空间并不在虚拟机里面,而是直接使用本地内存。
Heap
PSYoungGen total 2048K, used 1169K [0x00000000ffd80000, 0x0000000100000000, 0x0000000100000000)
eden space 1536K, 76% used [0x00000000ffd80000,0x00000000ffea4738,0x00000000fff00000)
from space 512K, 0% used [0x00000000fff80000,0x00000000fff80000,0x0000000100000000)
to space 512K, 0% used [0x00000000fff00000,0x00000000fff00000,0x00000000fff80000)
ParOldGen total 5632K, used 5616K [0x00000000ff800000, 0x00000000ffd80000, 0x00000000ffd80000)
object space 5632K, 99% used [0x00000000ff800000,0x00000000ffd7c1a8,0x00000000ffd80000)
Metaspace used 3213K, capacity 4496K, committed 4864K, reserved 1056768K
class space used 349K, capacity 388K, committed 512K, reserved 1048576K
- PSYoungGen:新生代情况
- PSOldGen:老年代情况,占用空间10240K,10M,这个是老年代的活跃数据情况
- Metaspace:元空间。(JDK7 以前为PSPermGen:永久带情况)
- Eden Space (heap): 内存最初从这个线程池分配给大部分对象。
- Survivor Space (heap):用于保存在eden space内存池中经过垃圾回收后没有被回收的对象。
- Tenured Generation (heap):用于保持已经在 survivor space内存池中存在了一段时间的对象。
- Permanent Generation (non-heap): 保存虚拟机自己的静态(refective)数据,例如类(class)和方法(method)对象。Java虚拟机共享这些类数据。这个区域被分割为只读的和只写的,
- Code Cache (non-heap):HotSpot Java虚拟机包括一个用于编译和保存本地代码(native code)的内存,叫做“代码缓存区”(code cache)
四:Jvm参数
- Trace跟踪参数
a.可以打印GC的简要信息
-verbose:gc
-XX:+printGC
b.打印GC详细信息
-XX:+PrintGCDetails
c.打印CG发生的时间戳
-XX:+PrintGCTimeStamps
d.指定GC log的位置,以文件输出
-Xloggc:log/gc.log
e.每一次GC后,都打印堆信息
-XX:+PrintHeapAtGC
f.监控类的加载
-XX:+TraceClassLoading
g.打印类的直方图
-XX:+PrintClassHistogram
按下Ctrl+Break后,打印类的信息:分别显示:序号、实例数量、总大小、类型 - Java堆的参数
a.Xms:初始堆大小,默认物理内存的1/64(<1g)
b.Xmx:最大堆大小,默认物理内存的1/4(<1G)
建议xms=xmx,好处是避免每次gc后,调整堆的大小,减少系统内存分配开销
c.Xmn:新生代大小,默认整个堆的3/8
d.-XX:NewRatio:新生与老年代的比值,不包含持久代
如果xms=xmx,且设置了xmn的情况下,该参数不需要设置
e.-XX:SurvivorRatio:Eden区和Survivor区的大小比值,设置为8,则两个Survivor区与一
个Eden区的比值为2:8,一个Survivor占整个新生的1/10
f.新建的对象也有可能直接进入老年代,比如:大对象,可通过设置-XX:PretenureSizeThreshold=1024(单位为字 节,默认为0)来代表超过多大时就不在新生代分配,而是直接在老年代分配
g. -XX:+HeapDumpOnOutOfMemoryError:OOM时导出堆到文件
h. -XX:+HeapDumpPath:导出OOM的路径
i. -XX:OnOutOfMemoryError:在OOM时,执行一个脚本,例如:
-XX:OnOutOfMemoryError=D:/mytest.bat %p
推荐配置
- 根据实际应用来调整新生代和幸存代的大
- 官方推荐新生代占堆的3/8
- 幸存代占新生代的1/10
- 在OOM时,记得Dump出堆,确保可以排查现场问题
3.Java栈的参数
-Xss
通常只有几百K
决定了函数调用的深度
4.元空间的参数
a.-XX:MetaspaceSize初始空间大小,达到该值就会触发垃圾收集进行类型卸载,同时GC会对该值进行调整:如果释放了大量的空间,就适当降低该值;如果释放了很少的空间,那么在不超过MaxMetaspaceSize时,适当提高该值
b.-XX:MaxMetaspaceSize最大空间,默认是没有限制的
c.-XX:MinMetaspaceFreeRatio在GC之后,最小的Metaspace剩余空间容量的百分比,减少为分配空间所导致的垃圾收集
e.-XX:MaxMetaspaceFreeRatio在GC之后,最大的Metaspace剩余空间容量的百分比,减少为释放空间所导致的垃圾收集