Java运行时内存分类

Java运行时内存分类

Java虚拟机在运行Java程序的时候会将代码中不同的部分分别放到相对应的内存区域,这些内存区域各有用途,搞清楚Java程序运行时的内存分配,就能够更清楚的理解Java程序的执行过程,也能够使开发者编写出更加高效的程序。

Java运行时数据区域

先来看下Java运行时内存的分类

  • 程序计数器(program counter register)
    主要作用是存放当前指令所在的单元地址的地方。当执行的是java方法时,程序计数器就是记录当前字节码指令的位置,如果是执行native方法时,是“undefined”。比如程序执行时遇到if语句了,字节码解释器会通过改变程序计数器的值就可以执行符合条件的if语句块。或者当某个线程阻塞被唤醒后,会继续执行,程序计数器就记录着上次阻塞时的位置。每个线程都有自己独立的程序计数器,这样各个线程在运行的时候就不会互相影响,这类内存有时也被称为“线程私有内存”。
  • Java虚拟机栈(JVM stack)
    程序在调用方法时会将该方法压栈,即将这个方法放到Java虚拟机栈里面,在这里面可以存储方法中的基本数据类型和对象的引用(注意不是对象,而是对象的一个引用,或者说是对象的指向)。它是线程私有的,生命周期与线程一致。可以通过下面参数设置java虚拟机栈的大小为180k
  • -Xss180k
  • 本地方法栈(native method stack)
    这部分跟Java虚拟机栈的作用类似,只不过本地方法栈主要是为调用native方法服务的,当程序调用native方法时会使用本地方法栈。
  • Java堆(java heap)
    这部分区域的主要作用就是存储类实例的(就是创建出来的对象),数组也会放到这里。它是可以被多个线程所共享的。在程序运行的时候会创建大量的对象,这些对象相对来说会占用比较多的内存,因此垃圾收集器主要管理的就是堆中的数据。
    在堆中可以再分为下面几块区域

    • 年轻代(young generation)
      新创建的对象会放在这个年轻代的eden区域中,经历一次垃圾回收之后,存活下来的对象会放到年轻代的survival区域中。
    • 老年代(old generation)
      survival区域中存活一段时间的对象会被移动代老年代区域中。
  • 方法区(method area)
    有的地方也称之为永久代(Permanent Generation),这样叫法不严谨,它是Java堆逻辑的一部分,但是他有着另一个名称,叫做非堆(Non-heap),单看名字的话,方法区会让人联想到方法,但是这部分区域跟方法的关系不太大,它主要存放类的基本信息(字段和方法数据以及方法和构造函数的代码,其实就是class文件),常量,静态变量等数据,这部分的垃圾回收工作不太好做。这部分区域中的数据是可以被多个线程访问的。java字符串常量池曾经在方法区中,在jdk7中被移至了JVM堆中。有的地方也称方法区为永久代(Permanent Generation),这个叫法不严谨,以前在方法区中有一块区域叫做永久代,不过曾经被称为永久代(permanent generation)区域已经在jdk8中被metaspace替换了,即永久代已经不存在了,曾经在永久代的字符串常量池也被移至jvm堆中。
  • 运行时常量池(runtime constant pool)
    运行时常量池是方法区进行分配的,主要存放字面量和符号引用等,关于符号引用,将java源文件文件编译成class文件之后,jvm并不知道该class文件使用的其他类的内存地址,例如:在com.monkey1024.Test类中使用了com.monkey1024.DBUtil类,此时就可以用com.monkey1024.DBUtil(假设)这个符号引用来表示这个DBUtil类的实际内存地址。

会发生OutOfMemoryError异常的区域:常量池、方法区、堆、Java虚拟机栈、本地方法栈。
会发生StackOverflowError异常的区域:Java虚拟机栈、本地方法栈。
程序计数器不会发生上面的异常。

jconsole简介

jconsole是sun公司提供的一款JDK自带的可视化虚拟机监控工具,在bin目录中有jconsole.exe,启动后可以看到该监控工具的运行界面。里面可以看到Java程序运行时,内存、线程、类的一些信息。

通过下面代码来演示一个OutOfMemoryError。
下面程序可以在jconsole中看到堆内存的使用率快速上升。

public static void main(String[] args) {
    List<Object> list = new LinkedList<>();
    while(true) {
        Object o = new Object();
        list.add(o);
        System.out.println(o);
        System.out.println(list.size());
    }
}

上面程序出现OutOfMemoryError的错误需要等一段时间,为了能够快速看到错误信息,可以将运行时参数设置如下:

-Xms5m -Xmx5m

表示JVM初始分配的堆内存和最大允许分配的堆内存都是5m,将堆内存设置小一些,可以快速看到OutOfMemoryError的报错信息。