JVM学习笔记(一)内存区域

一.jvm内存结构

JVM学习笔记(一)内存区域_jvm

线程私有

  • 虚拟机栈
  • 本地方法栈
  • 程序计数器

线程共享

  • 方法区
  • 直接内存区

二.程序计数器

作用:

  • 字节码的行号指示器: 字节码解释器通过改变程序计数器来依次读取指令,实现代码的流程控制:顺序执行,选择,循环,异常处理
  • 程序计数器用于记录当前线程执行的位置,帮助应用进行线程间切换

唯一不会出现OutOfMemoryError内存区域,生命周期线程创建而创建,线程结束而死亡

三.java虚拟机栈

特点:

  • 线程私有
  • 生命周期线程创建而创建,线程结束而死亡

方法调用的数据通过栈传递,每一个方法调用都会进入对应的栈帧被压入栈中,每一个方法调用结束后都会有一个栈帧弹出

每个栈帧:

  • 局部变量表
  • 操作数栈
  • 动态链接
  • 方法返回地址

JVM学习笔记(一)内存区域_本地方法_02

局部变量表

  • 编译期间可知的各种数据类型(boolean,byte,char,short,int,float,long,double),对象引用(rerence类型,引用指针或对象句柄)

操作数栈

  • 方法调用的中转站,存放方法执行过程中产生的中间计算结果或者临时变量

动态链接-------- 一个方法需要调用其他方法的场景

  • java源文件被编译成字节码文件时,所有的变量和方法引用都作为符号引用(Symbilic Renference)保存在Class文件的常量池
  • 当一个方法要调用其他方法,需要将常量池指向方法的的符号引用转化为其在内存地址中的直接引用。
  • 为了将符号引用转换为调用方法的直接引用

四.StackOverFlowError

  • StackOverFlowError:若栈内存大小不允许动态扩展,那么当线程请求栈的深度操作java虚拟机栈的最大深度,就会抛出​​StackOverFlowError​
  • OutOfMemoryError:若栈内存大小可以动态扩展,如果虚拟机在动态扩展栈的时候无法申请到足够的内存空间,则抛出​​OutOfMemoryError​

五.本地方法栈

  • 执行Native方法
  • 本地方法被执行的时候,在本地方法栈也会创建一个栈帧,用于存放该本地方法的局部变量表、操作数栈、动态链接、出口信息。
  • 方法执行完毕后相应的栈帧也会出栈并释放内存空间,也会出现​​StackOverFlowError​​​ 和​​OutOfMemoryError​​ 两种错误

六.堆

存放对象实例,所有对象实例以及数组都在这里分配内存

因为是垃圾回收器管理的主要区域,因此也被称为GC堆(Garbage Collected Heap)

  1. java.lang.OutOfMemoryError: GC Overhead Limit Exceeded
  2. java.lang.OutOfMemoryError: Java heap space

七.方法区

  • 方法区属于是JVM运行时数据区域的一块逻辑区域,是各个线程共享的内存区域
  • 虚拟机要使用一个类时候,需要读取并解析Class文件获取相关信息,将信息存入方法区。方法区会存储已被虚拟机加载的
  • 类信息,字段信息,方法信息,常量,静态变量,即时编译器编译后的代码缓存等数据

JVM学习笔记(一)内存区域_java_03

八.为什么要将永久代(PermGen)替换为元空间(MetaSpace)

  1. 永久代JVM本身设置固定大小上线,无法进行调整,元空间使用直接内存
  2. 元空间里面存放的是类的元数据,由系统空间决定

九.运行时常量池

  • Class文件中除了有类的版本,字段,方法,接口等描述信息外,还有存放编译期生成的各种字面量和符号引用的常量池表

十.字符串常量池

  • JVM提升性能和减少内存消耗针对字符串(String类)专门开辟的一块区域,避免字符串的重复创建
  • JDK1.7 之前,字符串常量池存放在永久代。JDK1.7 字符串常量池和静态变量从永久代移动了 Java 堆中。
  • JVM学习笔记(一)内存区域_java_04

十一.JDK1.7 为什么要将字符串常量池移动到堆中?

  • 永久代GC回收效率低,只有在Full GC时候才会执行
  • java程序通常有大量的被创建的字符串等待回收,将字符串常量池放到堆中,能够更高效及时地回收字符串内存。

十二.直接内存

直接内存的分配不会受到 Java 堆的限制,但是,既然是内存就会受到本机总内存大小以及处理器寻址空间的限制。