Java和C++当属目前最为流行的面向对象语言,C++的核心在于其高效率,很多工作都由程序员自己定义编写,Java的核心在于其将很多工作交给了JVM管理,这篇文章主要内容就是介绍JVM内部的机制。

内存布局

先看图:

  1. 程序计数器:可看作是当前线程所执行的字节码的行号指示器
  2. 虚拟机栈:每个方法在执行的同时都会创建一个栈桢(stack frame)用于存储局部变量表、操作数栈、动态链接、方法出口等信息。每个方法从调用直到执行完成的过程,就对应着一个栈桢在虚拟机栈中入栈到出栈的过程。局部变量表存放了编译期可知的各种基本数据类型、对象引用
  3. 本地方法栈:可看成是为本地Native方法的虚拟机栈
  4. 方法区:已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据(元数据)
  5. 堆:所有的对象实例以及数组都在堆上分配

直接内存:不是虚拟机运行时数据区的一部分,也不是Java虚拟机规范中定义的内存区域。JDK1.4中new IO类引入了一种基于通道(channel)与缓存区(buffer)的IO方式,它可以调用Native函数库直接分配堆外内存,然后通过一个存在堆中的DirectByteBuffer对象作为这块内存的引用进行操作。

对象的创建过程

  1. new指令 –> 检查类是否被加载过,若没有,先执行加载
  2. 分配内存(对象所需内存在内加载完成后,就可确定)
  3. 内存空间初始化为零值(不包括对象头)
  4. 设置对象头信息
  5. 执行初始化方法
  6. 对象引用入栈

为对象分配内存就是把一块确定大小的空间从Java堆中划分出来。有两种方法:

第一种,指针碰撞。适用于内存为规整的情况,即所有用过的内存放一边,空闲内存放另外一边,中间保留一个指针作为分界点;这种情况下,内存分配就是把指针向空闲那边移动对象大小相等的距离。适用于使用复制算法和标记整理算法的内存区域。

第二种,空闲列表。适用于内存不规整情况,虚拟机维护列表记录哪些内存可用,哪些不可用。适用于使用标记清除算法(CMS)的内存区域。

为对象分配内存空间的动作,在并发的情况下,也不一定是线程安全的:假如虚拟机正在给A分配,指针还没有修改,B使用了原来的指针来分配,这样就出现错误了。

解决内存分配的问题有两种方法:

  1. 虚拟机默认采用CAS配上失败重试的方式
  2. 把内存分配的动作按照线程划分在不同的空间之中进行——每个线程在Java堆中预先分配一小块内存(叫本地线程分配缓存 Thread Local Allocation Buffer TLAB)。这样哪个线程需要分配内存,就在哪个线程的TLAB上分配。可通过-XX:+/-UseTLAB来设定是否使用TLAB