JVM——内存分配与内存溢出

《深入理解Java虚拟机》出第三版了,工作一年多的时候看过第二版,重新复习一遍JVM

一、内存分配

JVM管理的内存包括5个运行时数据区域。

  • 线程私有:程序计数器,虚拟机栈,本地方法栈
  • 线程共享:堆,方法区

1、程序计数器PC

①程序计数器的作用

JVM中程序计数器是一块很小的内存空间,它可以看做是当前线程所执行的字节码的行号指示器。

即Java文件编译成class文件后,会被编译成很多字节码指令,例如iadd,istore等,

程序计数器是用来记录当前线程正在执行的字节码指令的地址,如果执行的是本地方法,计数器为Undefined

它是程序控制流的指示器,分支、循环、跳转、异常处理、线程恢复等都是需要依靠PC来完成的

PS : CPU中也有一个PC寄存器,作用是保存下一条CPU指令的地址,默认是每次执行指令后+1。

②程序计数器的由来

JVM的多线程是通过线程轮流切换、分配处理器执行时间的方式来实现的(《操作系统原理》中进程调度策略:时间片轮转法),

单核处理器在一个时间片内,仅能处理一个线程中的字节指令,时间片结束后,进入下一个时间片,单核处理器会处理另一个线程的字节码指令,

为了线程切换后能恢复到正确的位置,每条线程都需要有一个独立的程序计数器,独立存储--即程序计数器是线程私有的内存。

③内存溢出

程序计数器是唯一一个没有规定任何OutOfMemoryError情况的内存区域。

2、Java虚拟机栈

Java虚拟机栈也是线程私有的,它的生命周期与线程相同。

① Java虚拟机栈的作用

虚拟机栈描述的是Java方法执行的线程内存模型:每个方法被执行的时候,都会同步创建一个栈帧,用于存储局部变量表、操作数栈、动态链接、方法出口等信息。每个方法被调用直至执行完毕的过程,对应着一个栈帧在虚拟机栈中入栈到出栈的过程

② 局部变量表

局部变量表:存放编译期可知的各种Java虚拟机

  • 基本数据类型(boolean,byte,char,short,int,float,long,double)、
  • 对象引用类型(reference:指向对象起始地址的指针或者句柄)
  • returnAddress(指向一条字节码指令的地址),

局部变量表是以槽(Slot)为单位表示的,Slot大小为32bit,PS:long与double大小是64bit,需要两个Slot存储,不具备原子性,不是线程安全的。

局部变量表所需的内存空间在编译期间完成分配的,即编译期已经固定了内存大小,运行期间不会改变局部变量表的大小。

//很有意思的问题,下面两种写法中,
//在编译期就已经确定了栈帧中局部变量表的大小
public void test(){
    for(int i = 0; i < 10000; i++){
        //这里不会创建10000个reference引用,每次超出作用域范围,旧的obj都会被覆盖
        //所以跟下面写法,区别不是很大,不会引起栈溢出
        //优点可能就是,尽可能缩小引用的作用域范围
        Object obj = new Object();
    }
}

public void test(){
    Object obj;
    for(int i = 0; i < 10000; i++){
        //这里引用的作用与范围,比上面要广
        //但是由于没有引用所以,object对象GC可能会快一点。
        obj = new Object();
    }
}

③内存溢出

虚拟机栈区域会出现两种异常状况

栈溢出:如果线程请求的栈深度大于虚拟机所允许的站深度,将抛出StackOverflowError异常

内存溢出:如果Java虚拟机栈容量可以动态扩展,当栈扩展时无法申请到足够的内存会抛出OutOfMemoryError异常。

常见的HotSpot虚拟机栈是不可以动态扩展,所以不会由于扩展而导致的OOM异常,即只要线程申请栈空间成功之后就不会有OOM异常,申请栈失败还是会发生OOM异常的

3、本地方法栈

本地方法栈返回的作用与虚拟机栈的作用非常类似,线程私有的,并且本地方法栈为仅为虚拟机用到的本地方法服务。

与虚拟机栈相同,本地方法栈也会出现栈溢出与内存溢出。

常见的HotSpot虚拟机将本地方法栈和虚拟机栈合二为一了

4、堆

①堆的作用

Java Heap是JVM中所管理内存中最大的一块,是线程共享的。

Java Heap是在JVM启动时创建的,它的唯一作用就是存放对象实例。《Java虚拟机规范》中“所有的对象实例以及数组都应当在堆上分配”。

Java Heap是垃圾收集器管理的内存区域,所以也称为“GC堆”,GC会在后面学习

②TLAB

TLAB全称Thread Local Allocation Buffer,从分配内存的角度看,所有的线程共享的Java堆中可以划分出多个线程私有的分配缓存区TLAB。

TLAB的作用就是保证A线程创建Object,B线程创建Object在堆中的内存的

5、方法区

二、内存溢出

猜你喜欢

转载自www.cnblogs.com/wqff-biubiu/p/12747210.html