Java内存管理

运行时数据区域包括程序计数器,java虚拟机栈,本地方法栈,方法区和堆。其中,java虚拟机栈,本地方法栈,程序计数器是每个线程私有的。

字节码文件经过类加载子系统从静态存储结构转化为方法区的运行时内存结构。运行时数据区域包括程序计数器,java虚拟机栈,本地方法栈,方法区和堆。其中,java虚拟机栈,本地方法栈,程序计数器是每个线程私有的,方法区以及堆是所有线程共享的。

img

所有线程共享。

主要存放对象以及数组对象的。

但是,由于现在逃逸技术的存在,对象并不一定都是存在于堆,还有可能存放在栈上。

java堆分为新生代和老年代,新生代又分为Eden区以及Survivor区。

当内存不足时,会产生OOM异常。

Java虚拟机栈

线程私有,每一个线程都有一个自己的栈。

线程每执行一个方法,都会创建一个栈帧,用于存储局部变量表(对象引用,基本数据类型)等。方法调用直至完成的过程中,就是一个栈帧入栈和出栈的过程。
title
两种异常情况:

  • 当线程请求的栈深度超过最大值,会抛出 StackOverflowError 异常;
  • 栈进行动态扩展时如果无法申请到足够内存,会抛出 OutOfMemoryError 异常。可以通过 -Xss 这个虚拟机参数来指定每个线程的 Java 虚拟机栈内存大小:
1
java -Xss512M HackTheJava

本地方法栈

与Java虚拟机栈类似,只不过一个是为Java方法服务,一个是为Native方法服务,其他都一样。Native方法就是Java调用非Java代码的接口。例如调用C语言实现的接口。

程序计数器

可以看作是当前线程执行到的字节码的行号指示器。对于Java的多线程,为了使程序每次切换后能够恢复到正确的执行位置,因此每一个线程必须要有自己独立的程序计数器。如果线程执行的是Java方法,记录的是正在执行的虚拟机字节码指令的地址(如果正在执行的是本地方法则为空)。

方法区

各个线程共享的区域。存放的是虚拟机加载的类信息,常量,静态变量

在HotSpot中,永久代是方法区的实现。因为GC分代收集拓展到方法区。方法区主要是废弃类和常量的收集,对于方法区,也可以选择不进行垃圾回收。

一般来说,方法区不进行垃圾收集。

在jdk1.8之后,HotSpot中,删去了永久代,永久代的相关信息存放在了元空间

元空间与永久代最大的不同就是,元空间并不在JVM中,而是在本地内存。主要原因还是因为永久代的大小难以确定,容易发生OOM,而移到元空间,只会受到本地内存大小的限制。

运行时常量池

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

class常量池:我们写的每一个Java类被编译后,就会形成一份class文件;class文件中除了包含类的版本、字段、方法、接口等描述信息外,还有一项信息就是常量池(constant pool table),用于存放编译器生成的各种字面量(Literal)和符号引用(Symbolic References)。

title

class常量池在类加载完成后就会放入运行时常量池存放。

字符串常量池:存放字符串,位于堆内。常量池中同时存在字符串常量和字符串引用。直接赋值和用字符串调用String构造函数都可能导致常量池中生成字符串常量;而intern()方法会尝试将堆中对象的引用放入常量池

类加载子系统

类加载子系统负责从文件系统或者网络中加载 Class 信息,加载的类信息存放于一块称 为方法区的内存空间。

局部变量表所需要的内存空间在编译阶段就分配完毕。

对象的创建过程

  • 虚拟机遇到一个new指令,首先从常量池中获取这个类的符号引用,检查这个符号引用代表的类是否被加载。

  • 要是没有被加载,必须执行相应的类加载的过程。

  • 为对象分配内存,主要有指针碰撞和空闲列表两种办法。指针碰撞是所有使用过的内存放一边,未使用过的内存放一边,中间一个指针作为分界线,当为一个对象分配内存的时候,直接移动指针即可。空闲列表适用于空闲内存和使用过的内存互相交错,内存的使用情况都存在一个表上,根据这个表再去分配内存。指针碰撞适用于标记-整理算法,空闲列表适用于标记-清除算法。

  • 但是,如何解决分配内存过程中的并发问题呢?

    一个就是同步加锁

    另一个就是TLAB的使用,预先给每个线程在Java堆中都分配了一小块内存,哪个线程要给对象分配内存,直接在自己的TLAB中分配,当TLAB用完了,再分配新的,这一步才需要加锁。

  • 设置对象头

  • 执行init方法,初始化成员变量。

对象的内存布局

对象在内存中主要分为三个部分:对象头,实例数据以及对齐填充。

img

对象头主要结构是由Mark Word 和 Class Metadata Address 组成 。Class Metadata Address存储的是该对象属于类的地址,即可以判断这个对象属于哪一个类。

MarkWord有五种类型:
title

监控工具

VisualVM:监视线程,内存情况,堆栈情况,GC情况等.

VisualVM