JVM详解

各种JVM虚拟机

HotSpot

HotSpot VM 是Sun/OracleJDK和OpenJDK中的默认Java虚拟机,也是目前使用范围最广的Java虚拟机。在最初并非由Sun公司所开发,而是由一家名为“Longview Technologies”的小公司设计的。HotSpot既继承了Sun之前两款商用虚拟机的优点,也有许多自己新的技术优势,如它名称中的HotSpot指的就是它的热点代码探测技术。2006年,Sun陆续将SunJDK的各个部分在GPLv2协议下开放了源码,形成了Open-JDK项目,其中当然也包括HotSpot虚拟机。HotSpot从此成为Sun/OracleJDK和OpenJDK两个实现极度接近的JDK项目的共同虚拟机。Oracle收购Sun以后,建立了HotRockit项目来把原来BEA JRockit中的优秀特性融合到HotSpot之中。到了2014年的JDK 8时期,里面的HotSpot就已是两者融合的结果,HotSpot在这个过程里移除掉永久代,吸收JRockit的Java Mission Control监控工具等功能。

BEA JRockit JVM

JRockit虚拟机曾经号称是“世界上速度最快的Java虚拟机”,它是BEA在2002年从Appeal Virtual Machines公司收购获得的Java虚拟机。BEA将其发展为一款专门为服务器硬件和服务端应用场景高度优化的虚拟机,由于专注于服务端应用,它可以不太 关注于程序启动速度,因此JRockit内部不包含解释器实现,全部代码都靠即时编译器编译后执行。除此之外,JRockit的垃圾收集器和Java Mission Control故障处理套件等部分的实现,在当时众多的Java虚拟机中 也处于领先水平。
JRockit随着BEA被Oracle收购,现已不再继续发展,永远停留在R28版本,这是JDK 6版JRockit的代号。

IBM J9 VM

IBM J9虚拟机并不是IBM公司唯一的Java虚拟机,不过目前IBM主力发展无疑就是J9。与BEA JRockit只专注于服务端应用不同,IBM J9虚拟机的市场定位与HotSpot比较接近,它是一款在设计上全面考 虑服务端、桌面应用,再到嵌入式的多用途虚拟机。开发J9的目的是作为IBM公司各种Java产品的执行平台,在和IBM产品(如IBM WebSphere等)搭配以及在IBM AIX和z/OS这些平台上部署Java应用。从2016年起,IBM逐步将OMR项目和J9虚拟机进行开源,完全开源后便将它们捐献给了Eclipse基金会管理,并重 新命名为Eclipse OMR和OpenJ9。

内存溢出与内存泄露

  • 内存溢出,是指程序在申请内存时,没有足够的内存空间供其使用,出现out of memory;
  • 内存泄露,是指程序在申请内存后,无法释放已申请的内存空间,一次内存泄露危害可以忽略,但内存泄露 堆积后果很严重,无论多少内存,迟早会被占光。

内存管理

程序计数器 

程序计数器(Program Counter Register)是一块较小的内存空间,它可以看作是当前线程所执行的字节码的行号指示器。在虚拟机概念模型里(概念模型,各种虚拟机可能会通过一些更高效的方式实现),字节码解释器工作时就是通过改变这个计数器的值来选取下一条需要执行的字节码指令:分支、跳转、循环、异常处理、线程恢复等基础操作都会依赖这个计数器来完成。每个线程都有独立的程序计数器,用来在线程切换后能恢复到正确的执行位置,各条线程之间的计数器互不影响,独立存储。所以它是一个“线程私有”的内存区域。此内存区域是唯一一个在JVM规范中没有规定任何OutOfMemoryError情况的区域。

虚拟机栈

JVM栈是线程私有的内存区域。它描述的是java方法执行的内存模型,每个方法执行的同时都会创建一个栈帧(Stack Frame)用于存储局部变量表、操作数栈、动态链接、方法出口等信息。每个方法从调用直至完成的过程,都对应着一个栈帧从入栈到出栈的过程。每当一个方法执行完成时,该栈帧就会弹出栈帧的元素作为这个方法的返回值,并且清除这个栈帧,Java栈的栈顶的栈帧就是当前正在执行的活动栈,也就是当前正在执行的方法。就像是组成动画的一帧一帧的图片,方法的调用过程也是由栈帧切换来产生结果。

局部变量表存放了编译器可知的各种基本数据类型(int、short、byte、char、double、float、long、boolean)、对象引用(reference类型,它不等同于对象本身,可能是一个指向对象起始地址的引用指针,也可能是指向一个代表对象的句柄或其他与此对象相关的位置)和returnAddress类型(指向了一跳字节码指令的地址)。

在JVM规范中,对这个区域规定了两种异常情况:如果线程请求的栈深度大于虚拟机允许的深度,将抛出StackOverflowError异常;如果虚拟机栈可以动态扩展,在扩展时无法申请到足够的内存,就会抛出OutOfMemoryError异常。

本地方法栈

本地方法栈和虚拟机栈所发挥的作用是很相似的,它们之间的区别不过是虚拟机栈为虚拟机执行Java方法(字节码)服务,而本地方法栈则为虚拟机使用到的Native方法服务。Sun HotSpot 直接就把本地方法栈和虚拟机栈合二为一。本地方法栈也会抛出StackOverflowError和OutOfMemoryError异常。

Heap是OOM故障最主要的发源地,它存储着几乎所有的实例对象,堆由垃圾收集器自动回收,堆区由各子线程共享使用;通常情况下,它占用的空间是所有内存区域中最大的,但如果无节制地创建大量对象,也容易消耗完所有的空间;堆的内存空间既可以固定大小,也可运行时动态地调整,通过参数-Xms设定初始值、-Xmx设定最大值。

方法区

方法区是被所有线程共享的内存区域,用来存储已被虚拟机加载的类信息、常量、静态变量、JIT(just in time,即时编译技术)编译后的代码等数据。运行时常量池是方法区的一部分,用于存放编译期间生成的各种字面常量和符号引用。

运行参数

标准参数

使用java -help检索出所有的标准参数

-help 
-version

-X参数  (非标准参数)

通过java -X查看非标准参数

-Xmx2048m:等价于-XX:MaxHeapSize,设置JVM最大堆内存为2048M。 
-Xms512m:等价于-XX:InitialHeapSize,设置JVM初始堆内存为512M。

-Xint 
-Xcomp

-XX参数(使用率较高)

-XX参数也是非标准参数,主要用于jvm的调优和debug操作

-XX:newSize 
-XX:+UseSerialGC

常用命令

jps(查看当前用户下的java进程的pid及基本信息)

jstat 查看堆内存各部分的使用量,以及加载类的数量。

命令格式: jstat [-命令选项] [vmid] [间隔时间/毫秒] [查询次数]

jstat -class 12076类加载统计

jstat -complier 12076编译统计

jstat -gc 12076 垃圾回收统计

内存相关问题

  • 内存溢出,是指程序在申请内存时,没有足够的内存空间供其使用,出现out of memory;
  • 内存泄露,是指程序在申请内存后,无法释放已申请的内存空间,一次内存泄露危害可以忽略,但内存泄露 堆积后果很严重,无论多少内存,迟早会被占光。
public class TestJvmOutOfMemory {
   public static void main(String[] args) {
       List<Object> list = new ArrayList<>();
       for (int i = 0; i < 10000000; i++) {
           String str = "";
           for (int j = 0; j < 1000; j++) {
               str += UUID.randomUUID().toString();
       }
           list.add(str);
     }
       System.out.println("ok");
 } 
}

内存分析工具MAT(Memory Analyzer Tool)

官网地址:https://www.eclipse.org/mat/

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值