Java GC 基础一 ——Hotspot 架构及内存区域介绍

0. 前言


本文主要想阐述的问题如下:

    1. 什么是JVM(Java Virtual Machine)和GC(Garbage Collection)?
    1. HotSpot虚拟机架构
    1. JVM内存区域介绍
    1. Java对象的访问方式

面试常问的问题:

    1. 变量和实例存在哪?
    1. java栈的作用?
    1. java的堆存什么?
    1. 方法区存什么?
    1. 各种内存溢出的情况及其原因?

1. 什么是JVM?


  • VM(Virtual Machine):一种用于计算设备的规范,它是一个虚构出来的计算机,是通过在实际的计算机上仿真模拟各种计算机功能来实现的。
  • JVM(Java Virtual Machine):提供一个基于抽象规格描述的计算机模型,为解释程序开发人员提供很好的灵活性,同时也确保Java代码可在符合该规范的任何系统上运行

关于JVM,需要说明一下的是,目前使用最多的Sun公司的JDK中,自从1999年的JDK1.2开始直至现在仍在广泛使用的JDK6,其中默认的虚拟机都是HotSpot。2009年,Oracle收购Sun,加上之前收购 的EBA公司,Oracle拥有3大虚拟机中的两个:JRockit和HotSpot,Oracle也表明了想要整合两大虚拟机的意图,但是目前在新发布的JDK8中,默认的虚拟机仍然是HotSpot,因此本文中默认介绍的虚拟机都是HotSpot,相关机制也主要是指HotSpot的GC机制。

2. HotSpot虚拟机架构


Hotspot Architecture

这个架构可以分成三层看:

  • 最上层:javac编译器将编译好的字节码class文件,通过java 类装载器执行机制,把对象或class文件存放在 jvm划分内存区域
  • 中间层:称为Runtime Data Area,主要是在Java代码运行时用于存放数据的,从左至右为方法区(持久代也叫非堆)、堆(共享,GC回收对象区域)、栈、程序计数器和寄存器、本地栈(私有)
  • 最下层:JVM最核心两块 JIT(just in time)即时编译器 和 GC(Garbage Collection,垃圾回收器)

3. JVM内存区域

在Java运行时的数据区里,由JVM管理的内存区域分为下图几个模块:


image.png

3.1 Method area:

又叫永久代(Permanent Generation)或Non-Heap(非堆)。方法区中存放了每个Class的结构信息,包括常量池、字段描述、方法描述等等。
特点:

  • 与Java堆一样不需要连续的内存,也可以选择固定大小或者可扩展外,甚至可以选择不实现垃圾收集。
  • 相对来说,垃圾收集行为在这个区域是相对比较少发生的,但并不是不会发生GC,这里的GC主要是对常量池的回收和对类的卸载,
  • 回收的“成绩”一般也比较差强人意,尤其是类卸载,条件相当苛刻。

3.2 Heap:

堆区(Heap):创建实例对象,几乎所有对象实例都在这里分配内存。Java堆是内存回收的主要区域,所以也有人称他为GC堆。
特点:

  • 堆区由所有线程共享,在虚拟机启动时创建。
  • 堆区的存在是为了存储对象实例,原则上讲,所有的对象都在堆区上分配内存。
  • 堆内存需要在逻辑上是连续的(在物理上不需要),在实现时,可以是固定大小的,也可以是可扩展的,目前流的虚拟机都是可扩展的。如果在执行垃圾回收之后,仍没有足够的内存分配,也不能再扩展,将会抛出OutOfMemoryError:Java heap space异常。
  • 一个接口的多个实现类需要的内存大小可能不一样,一个方法中多个分支需要的内存也不一样,所以内存分配回收是动态的。

3.3 Java Thread:

虚拟机栈(JVM Stack):Java方法被执行的时候的内存模型,一个线程的每个方法在执行的同时,都会创建一个栈帧(Statck Frame),栈帧中存储的有局部变量表、操作站、动态链接、方法出口等,当方法被调用时,栈帧在JVM栈中入栈,当方法执行完成时,栈帧出栈。
特点:

  • 局部变量表中存储着方法的相关局部变量,包括各种基本数据类型,对象的引用,返回地址等。
  • 局部变量表是在编译时就已经确定好的,方法运行所需要分配的空间在栈帧中,是完全确定的,在方法的生命周期内都不会改变。
  • 虚拟机栈中定义了两种异常,如果线程调用的栈深度大于虚拟机允许的最大深度,则抛出StackOverFlowError(栈溢出);不过多数Java虚拟机都允许动态扩展虚拟机栈的大小(有少部分是固定长度的),所以线程可以一直申请栈,直到内存不足,此时,会抛出 OutOfMemoryError(内存溢出)。
  • 每个线程对应着一个虚拟机栈,因此虚拟机栈也是线程私有的。

3.4 Program Counter Registers

每个JAVA线程都有一个单独的PC Register,他是一个指针,由Execution Engine读取下一条指令。对于非Native方法,这个区域记录的是正在执行的VM原语的地址,如果正在执行的是Natvie方法,这个区域则为空(undefined)。由于程序计数器只是记录当前指令地址,所以不存在内存溢出的情况,所以此内存区域是唯一一个在VM内存区域中,没有规定任何OutOfMemoryError情况的区域。
特点:

  • 每个程序计数器只用来记录一个线程的行号,所以它是线程私有(一个线程就有一个程序计数器)的

3.5 Native Method Stack

供本地方法(非java)使用的栈。每个线程持有一个Native Method Stack。本地方法栈在作用、运行机制、异常类型等方面都与虚拟机栈相同,唯一的区别是:虚拟机栈是执行Java方法的,而本地方法栈是用来执行native方法的,在很多虚拟机中(如Sun的JDK默认的HotSpot虚拟机),会将本地方法栈与虚拟机栈放在一起使用。
特点:

  • 本地方法栈也是线程私有的。

4 Java对象的访问方式

JAVA是面向对象的语言,那么在JAVA虚拟机中,存在非常多的对象,对象访问是无处不在的。即时是最简单的访问,也会涉及到JAVA栈、JAVA堆、方法区这三个非常重要的内存区域之间的关联关系。

比如:

Object obj = new Object();

其中,Object obj这部分语义作为一个reference类型数据出现,将存储到JAVA栈
的本地变量表中。new Object()将生成一个实体对象,存储在JAVA堆中,包含了Object类型的所有实例数据值(对象中各个字段的数据)的结构化内存,根据具体类型以及虚拟机实现的对象内存布局的不同,这块内存的长度是不固定的。另外,在JAVA堆中还必须包含能查找到此对象类型数据(如对象类型、父类、实现接口、方法等)的地址信息,这些类型数据存储在方法区中。不同虚拟机实现的对象访问方式会有所不同,主流的访问方式有两种:句柄和直接指针。

  • 句柄访问方式
    JAVA堆中将会划分出来一块内存作为句柄池,reference中就是存储了对象的句柄地址,而句柄中包含了对象实例数据类型数据各自的具体地址信息。
    • 好处:
      reference中存储的是稳定的句柄地址,在对象被移动时,只会改变句柄中的实例数据指针,而reference本身不需要被修改。
句柄访问方式
  • 直接指针
    相比较句柄的访问方式,JAVA堆中不会单独划分内存,reference中直接存储了对象地址,而对象中包含了对象类型数据的地址信息。

    • 好处:
      速度更快,节省了一次指针定位需要的时间开销,由于JAVA对象访问十分频繁,这类开销积小成多后也是一项非常可观的执行成本。Sun HotSpot虚拟机使用的就是这种访问方式。
image.png

可能上述文字比较抽象,我们举个例子,比如有这样一个实体类,名为Stu:

public class Stu extends Object{
  private String name;
  private int age;
  public Stu(String name,String age){
    this.name = name;
    this.age = age;
  }
  public String getName(){
    return this.name;
  }
  ...
}

创建Stu对象:

    Stu kevin = new Stu(“kevin”,15);

这样根据上文解释如下:

kevin作为一个reference类型的变量存储在本地变量表中,在Hotspot虚拟机中,存储的是具体对象(kevin)的直接地址;new Stu(“kevin”,15)就是实例化了一个对象,JAVA堆中保存了Stu实体类的所有的字段信息,比如name=”kevin”,age=15。此时,JAVA堆中还存储了Stu对象的类型数据的地址信息,通过这个地址在方法区中可以查找对象的类型、父类、实现的接口、方法等信息。

备注:

  • Execution Engine :是执行引擎。Class文件被加载后,即时编译器(JIT compiler,just-in-time compiler)会把Java的字节码(包括需要被解释的指令的程序)转换成可以直接发送给处理器的指令的程序。
  • Native Method :一个Native Method就是一个java调用非java代码的接口。一个Native Method是这样一个java的方法:该方法的实现由非java语言实现,比如C。这个特征并非java所特有,很多其它的编程语言都有这一机制,比如在C++中,你可以用extern "C"告知C++编译器去调用一个C的函数。
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,406评论 6 503
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,732评论 3 393
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 163,711评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,380评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,432评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,301评论 1 301
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,145评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,008评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,443评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,649评论 3 334
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,795评论 1 347
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,501评论 5 345
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,119评论 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,731评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,865评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,899评论 2 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,724评论 2 354