JVM-Java内存区域

Java虚拟机在执行java程序时,会把内存划分为以下区域

JVM-Java内存区域


1.程序计数器

程序计数器是属于线程私有的,正在执行java程序时,是保存虚拟机字节码指令的地址。执行本地方式时,则为null;

用于线程在切换时,恢复到正确的执行位置。

2.java虚拟机栈

java虚拟机栈也是线程私有的,生命周期和线程相同。用于储存局部变量表、操作数栈、动态链接、方法出口。

局部变量表包含数据基本类型、对象引用(指针或句柄)、和returnAddress类型(指向字节码指令地址)

3.本地方法栈

保存该线程所使用的本地方法服务。

4.java堆

存放对象实例,是线程共享的。内存回收主要作用于这一块,可以细分为新生代,老年代等。

5.方法区

方法区是线程共享的,用于存储虚拟机加载的类信息、常量、静态变量、即时编译后的代码等数据。也成为永生代。

运行时常量池是方法区的一部分。

有三个概念需要清楚:

  • 常量池(Constant Pool):常量池数据编译期被确定,是Class文件中的一部分。存储了类、方法、接口等中的常量,当然也包括字符串常量。

  • 字符串池/字符串常量池(String Pool/String Constant Pool):是常量池中的一部分,存储编译期类中产生的字符串类型数据。

  • 运行时常量池(Runtime Constant Pool):方法区的一部分,所有线程共享。虚拟机加载Class后把常量池中的数据放入到运行时常量池。

  1. 常量池:可以理解为Class文件之中的资源仓库,它是Class文件结构中与其他项目资源关联最多的数据类型。

  2. 常量池中主要存放两大类常量:字面量(Literal)和符号引用(Symbolic Reference)。

  3. 字面量:文本字符串、声明为final的常量值等;

  4. 符号引用:类和接口的完全限定名(Fully Qualified Name)、字段的名称和描述符(Descriptor)、方法的名称和描述符

JDK1.6之前字符串常量池位于方法区之中。 

JDK1.7字符串常量池已经被挪到堆之中。


对象的创建

1.相应类加载检查过程

       JVM(本文特指HotSpot)遇到new指令时,先检查指令参数是否能在常量池中定位到一个类的符号引用

       (A)、如果能定位到,检查这个符号引用代表的类是否已被加载、解析和初始化过;

       (B)、如果不能定位到,或没有检查到,就先执行相应的类加载过程

2、为对象分配内存

      对象所需内存的大小在类加载完成后便完全确定(JVM可以通过普通Java对象的类元数据信息确定对象大小);

      为对象分配内存相当于把一块确定大小的内存从Java堆里划分出来

(A)、分配方式:

(I)、指针碰撞

      如果Java堆是绝对规整的:一边是用过的内存,一边是空闲的内存,中间一个指针作为边界指示器;

      分配内存只需向空闲那边移动指针,这种分配方式称为"指针碰撞"(Bump the Pointer);

(II)、空闲列表

      如果Java堆不是规整的:用过的和空闲的内存相互交错;

      需要维护一个列表,记录哪些内存可用;

      分配内存时查表找到一个足够大的内存,并更新列表,这种分配方式称为"空闲列表"(Free List);

      Java堆是否规整由JVM采用的垃圾收集器是否带有压缩功能决定的

      所以,使用Serial、ParNew等带Compact过程的收集器时,JVM采用指针碰撞方式分配内存;而使用CMS这种基于标记-清除(Mark-Sweep)算法的收集器时,采用空闲列表方式;

      后面再介绍垃圾收集算法和垃圾收集器,了解垃圾收集时应注意这里的内容;

分配内存是非线程安全的,可以通过CAS或者是为线程分配缓冲解决。