第二章 Java内存区域和内存溢出异常

2.1 概述

Java不用自己回收内存,他会自己进行GC。


2.2 内存区域

GC的时候虚拟机把管理的内存划分了几块:

- 方法区
- 程序计数器
- 本地方法栈
- 虚拟机栈
- 堆 
2.2.1 程序计数器
  • 多线程时轮流切换至不同的线程,每个线程都有属于自己的程序计数器,用来记录接下来要执行的字节码指令。
  • 各个线程之间的程序计数器相互独立,因此这块也成为线程私有的内存。
  • 如果此时正在执行Java方法,那么这里边儿存的就是它的字节码指令地址;如果是native方法的话,这里就是空的。(native方法是指:使用Java去调用非Java的方法
  • 这个内存区域是唯一一个绝不会发生OOM的地方。
2.2.2 Java虚拟机栈
  • 线程私有,生命周期和线程一致。
  • 每调用一个方法就创建一个栈帧入栈,结束调用后出栈。
  • 这里存储一些局部变量,方法出口等信息。其中局部变量的大小是确定的,并且在编译时就可以完成内存分配;它包括一些基础类型和对象的引用指针或是句柄地址。运行时大小不会再改变。
  • 会产生两种异常。一个是线程请求栈深度过深(比方说递归太多),大于虚拟机允许时,会抛出栈溢出StackOverFlowError;二是内存不足时OOM。
2.2.3 本地方法栈

他和虚拟机栈的定义和规则都差不多。

  • 区别就是Java虚拟机栈为Java方法(也就是字节码)服务,本地方法栈为native方法服务。
  • 也会栈溢出和OOM
2.2.4 Java堆
  • 所有线程共享。
  • 他是逻辑上连续,物理上允许不连续。可固定大小,也可扩展。
  • Java虚拟器启动时创建。
  • 唯一的目的是用来存放对象实例。
  • 这里是发生GC最主要的区域。
  • 内存不足时会发生OOM。
2.2.5 方法区
  • 线程共享
  • 存放已被虚拟机加载的类信息、常量、静态变量等
  • 有个别名“永久代”
  • 不需要连续内存,大小可控,允许不GC
  • GC主要是对常量池的回收和类型卸载
  • 内存不足时OOM
2.2.6 运行时常量池(隶属于2.2.5.方法区)
  • 类加载后,存放编译期产生的字面量和符号引用
  • 运行期间可能会放入新的数据
  • 受方法区内存限制,会出现OOM
2.2.7 直接内存
  • 不属于Java虚拟机的数据区
  • 使用频繁
  • 受物理内存限制,也会出现OOM

2.3 对象访问

以 Object o = new Object()为例:

  • 会在Java栈中写入该对象的引用 (句柄或直接指针)
  • 堆中存储具体的结构化内存
  • 对象类型数据(对象类型、父类、接口)信息存储在方法区


    第二章 Java内存区域和内存溢出异常_第1张图片
    通过句柄访问对象

    第二章 Java内存区域和内存溢出异常_第2张图片
    通过直接指针访问对象

基于以上,句柄访问对象时,如果出现GC导致对象地址变动,只需要改变句柄池里指针,而不需要改动Java栈中的引用;直接指针访问的话,速度更快,节约一次指针定位的开销。sun hotSpot主要是第二种。

你可能感兴趣的:(第二章 Java内存区域和内存溢出异常)