JVM 性能调优(一)

jvm 性能优化一直是个老生常谈的问题,大部分面试也都会多少涉及到,通过浏览这篇文章,希望你能获得一些启发。本文基于 Hotspot虚拟机 jdk 1.7

JVM内存模型

JVM内存划分

JVM 内存划分 通常指的是运行时数据区域的划分,主要有

  • 程序计数器
  • java虚拟机栈
  • 本地方法栈
  • 方法区

程序计数器

程序计数器是一块较小的空间,可以看作是当前线程所执行的字节码的行号指示器,每个线程都有独立的计数器,如果当前线程正在执行一个java方法,那么计数器记录为证正在执行的虚拟机字节码指令的地址,如果执行的是native方法,则计时器值为空(undefined),这也是唯一一个在 java 虚拟机规范中没有规定任何OutMemoryError情况的区域

java虚拟机栈

和程序计数器一样也是线程私有的,每个方法在执行的同时都会创建一个栈帧用于存储局部变量表,和操作数、方法出口等,每一个方法从调用到执行完成的过程,就对应着一个栈帧在虚拟机栈中入展导出站的过程。
在虚拟机规范中,如果线程请求的栈深度大于虚拟机所允许的深度,将抛出StackOverflowError异常,如果可以动态扩展,但是扩展仍无法申请足够的内存,就会抛出OutOfMemoryError。

本地方法栈

本地方法栈同虚拟机栈相似,只不过服务的对象不同,本地方法栈为虚拟机用到的Native方法服务。(Sun HotSpot 直接把 本地方法栈和java虚拟机栈合二为一)

堆(heap)

堆是所有线程共享的一块内存区域, 在虚拟机启动时创建,目的单一 -> 存放对象;可以出狱不连续的内存空间,只要逻辑上连续即可,
堆又分为:

  • New(年轻代) 年轻代用来存放JVM刚分配的Java对象
  • Tenured(年老代) 年轻代中经过垃圾回收没有回收掉的对象将被Copy到年老代

NewTenured属于堆内存,堆内存会从JVM启动参数(-Xmx:3G)指定的内存中分配,-Xmx :最大堆内存, -Xms:初始化堆内存

New的内存划分
  • Eden:Eden用来存放JVM刚分配的对象
  • From Survivor
  • To Survivor

两个Survivor空间一样大,当Eden中的对象经过垃圾回收没有被回收掉时,会在两个Survivor之间来回Copy,当满足某个条件,比如Copy次数,就会被Copy到Tenured。显然,Survivor只是增加了对象在年轻代中的逗留时间,增加了被垃圾回收的可能性。

方法区

方法区和堆一样是各个线程共享的内存区域,主要用于存储一杯虚拟机加载的类信息,常量、静态变量、即时编译后的代码, 对于HotSpot虚拟机来说,是使用永久代来实现方法区。所以大家听到或看到很多说永久代就是方法区,其实就是这么来的,但两者本质上并不等价。
根据虚拟机规范: 当方法区无法满足内存分配时,将抛出OutOfMemoryError

  • 永久代(Perm) (jdk1.8 已废弃,改成 元空间)永久代存放Class、Method元信息,其大小跟项目的规模、类、方法的量有关,一般设置为128M就足够,设置原则是预留30%的空间。

Perm不属于堆内存,有虚拟机直接分配,但可以通过-XX:PermSize -XX:MaxPermSize 等参数调整其大小。

运行时常量池

运行时常量池是方法区的一部分, Class文件除了有类的版本号、字段、方法、接口等描述信息外,还有一项信息就是常量池,用于存放编译期生成的各种字面亮和符号。这部分内容将在类加载后进入方法区的运行时常量池中存放。

未完待续….

坚持原创技术分享,您的支持将鼓励我继续创作!