JVM 整体组成可分为以下四个部分:
运行时数据区为最主要部分
- 类加载器(ClassLoader)
- 运行时数据区(Runtime Data Area)
- 执行引擎(Execution Engine)
- 本地库接口(Native Interface)
方法区用于存储已经被虚拟机加载的类型信息、常量、静态变量、即时编译器编译后的代码缓存等。
方法去与Java堆一样,是各个线程共享的内存区域
方法区的大小和堆一样,可以选择固定的大小或者扩展
方法去的大小决定了系统可以保存多少各类,如果系统定义了太多的类,会导致方法去内存溢出。
查看JVM启动参数的值
java -XX:+PrintFlagsInitial 该命令可以查看所有JVM参数启动的初始值
可以通过以下的几个参数对metaspace进行控制:
-XX:metaspaceSize=N #这个参数是初始化的metaspace大小,该值越大触发metaspace GC的时机就越晚,默认值大概是20M。 -XX:MaxmetaspaceSize=N # 限制metaspace增长的上限,防止因为某些情况导致metaspace无限的使用本地内存,影响到其他程序,默认值大概是4个G。 -XX:MinmetaspaceFreeRatio=N #当进行过metaspace GC之后,会计算当前metaspace的空闲空间比,如果空闲比小于这个参数,那么虚拟机将增长metaspace的大小,默认值为40。 -XX:MaxmetasaceFreeRatio=N #当进行过metaspace GC之后, 会计算当前metaspace的空闲空间比,如果空闲比大于这个参数,那么虚拟机会释放metaspace的部分空间,默认值为70。 -XX:MaxmetaspaceExpansion=N # metaspace增长时的最大幅度,默认值为5452592B(大约为5MB)。 -XX:MinmetaspaceExpansion=N #metaspace增长时的最小幅度,默认值为340784B(大约330KB为)。1、类型信息 2、域信息 3、方法信息 4、 二.堆空间(Java Heap,线程共享)
Java 堆是内存空间占据的最大一块区域了,Java 堆是用来存放对象实例及数组,也就是说我们代码中通过 new 关键字 new 出来的对象都存放在这里。所以这里也就成为了垃圾回收器的主要活动营地了,于是它就有了一个别名叫做 GC 堆,并且单个 JVM 进程有且仅有一个 Java 堆。
PS:根据Java虚拟机的发展,JIT编译器的发展和逃逸分析技术的逐渐成熟,现在存在着new出来的对象不存在于堆中的可能性。比如创建在方法内的对象,未在方法外调用,会被优化为创建在虚拟机栈中,而不是堆中
JVM 提供了参数 -Xmn 来设置年轻代内存的大小,但没有提供参数设置老年代的大小。但其实老年代的大小就等于堆大小减去年轻代大小。
-Xmn :设置年轻代堆的大小2、年轻代(新生代) 1.Eden空间 2.From空间(survivor0) 3.To空间(survivor1) 3、老年代 4、堆空间中的GC过程
- 绝大多数的新New出来的对象都放在了Eden区域(伊甸园,新生地)
- Eden区域快满的时候进行一次清理(Minor GC,轻量级垃圾回收),不被引用的对象直接被清理掉,还在引用但是年龄较大的,挪到S0区域。
- Eden第二次快满的时候,清理操作,清除掉Eden中的未被引用的对象和S0中未被引用的对象,并将这些对象都挪到S1区域。(保证S0或者S1必然有一个为空,用来存储下一次接收的对象)
- Eden和S(0或者1)区域都快满的时候,会将新生代(Eden区域+S区域)挪到老年代的区域,
- 循环执行,直到老年代(Old)区域及新生代区域都快满的时候,会对内存区域进行一次大清洗(FullGC),为之后对象的创建,腾地方。
清理Eden区和Survivor区叫Minor GC;清理Old区叫Major GC;清理整个堆空间—包括年轻代和老年代叫Full GC。
内存不是越大越好,盲目增大堆内存可能会让吞吐量不增反减,堆内存大了,每次gc扫描对象也就越多也越需要花费时间,反而会适得其反。合理设置堆内存大小,根据实际业务调整,不宜过大,也不宜过小。
三.栈空间(Java Virtual Machine Stacks Area,线程隔离)栈是运行时的单位,队是存储的单位。
栈解决程序的运行问题,即程序如何执行,或者说如何处理数据,堆解决的是数据存储问题,即数据在哪里放,怎么放。
Java虚拟机栈早期称之为Java栈,每个线程在创建的时候,都会创建一个虚拟机栈,其内部保存着一个个的栈帧,对应着每一次的Java方法调用。
2、虚拟机栈的生命周期生命周期与线程一致
3、栈的结构 1.局部变量表(Local Variable Table)局部变量表(Local Variables)是一组变量值的存储空间,用于存放方法参数和方法内部定义的局部变量。在Java程序被编译为Class文件时,就在方法的Code属性的max_locals数据项中确定了方法所需的分配的局部变量表的最大容量。
2.操作数栈(Operand Stack)操作数栈(Operand Stack)也常被成为操作栈,是一个后入先出栈,用于保存计算过程中的中间结果,同时作为计算过程中变量临时的存储空间。其最大深度在编译时就被写到了Code属性的max_stacks中。
3.动态链接(Dynamic linking) 4.返回地址(Return Address) 5.栈帧数据(Stack Data) 3、作用主管Java程序的运行,保存方法的局部变量,部分结果,并参与方法的调用和返回。
4、特点 5、虚拟机栈大小的调整 四.PC寄存器(程序计数器,Program Counter Register,线程隔离)程序计数器是一块很小的内存空间,主要用来记录各个线程执行的字节码的地址,例如,分支、循环、跳转、异常、线程恢复等都依赖于计数器。由于 Java 是多线程语言,当执行的线程数量超过 CPU 核数时,线程之间会根据时间片轮询争夺 CPU 资源。如果一个线程的时间片用完了,或者是其它原因导致这个线程的 CPU 资源被提前抢夺,那么这个退出的线程就需要单独的一个程序计数器,来记录下一条运行的指令。
- PC寄存器是Java虚拟机中唯一的没有内存溢出的区域。
- PC寄存器是一块很小的内存空间,大小几乎可以忽略不计
- PC寄存器的线程是私有的,每个线程都有自己的PC寄存器,其生命周期同线程的生命周期完全一致
- 在执行Native方法(调用C语言方法)的时候,PC寄存器为未指定值(undefned)
红圈内称之为指令地址(偏移地址),由PC寄存器进行记录,指的是该线程下一步的操作是什么。
CPU处理着多个线程,在不同的线程之间来回切换,切换之后就需要知道接下来从哪里继续执行,PC寄存器中的数据就是告诉CPU下一个指令执行的是哪个。
五.本地方法栈(Native Method Stack,线程隔离) 四、执行引擎(Execution Engine) 五、本地库接口


