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

1、运行时数据区域

程序计数器:

  • 当前线程所执行的字节码的行号指示器,用于存放下一条需要运行的指令。
  • 运行速度最快位于处理器内部。
  • 线程私有。

虚拟机栈:

  • 描述的是Java方法执行的内存模型,用于存放对象的引用和基本数据类型。
  • 每个方法执行的时候都会创建一个栈帧(stack frame)用于存放 局部变量表、操作栈、动态链接、方法出口。
  • 线程私有,生命周期与线程相同。

方法栈:

  • 和虚拟机栈功能类似,管理本地的方法调用。
  • 虚拟机栈为虚拟机执行的Java方法的方法服务,方法栈则为虚拟机使用的本地方法的服务。

堆:

  • 虚拟机最大的一块区域,虚拟机启动的时候创建。
  • 用于存放对象的实例,所有对象实例和数据的在堆上分配内存。
  • 线程共享的区域。

方法区:

  • 用于存放一些类信息,常量,静态变量和即时编译后的代码等数据。
  • 线程共享的区域。

运行时常量池:

  • 方法区的一部分,用于存放编译期生成的各种字面量和符号引用。

直接内存:

  • 不属于运行时数据区,堆外内存。

2、HotSpot虚拟机对象探秘

对象的创建:

  • 接受new关键字指令后,检查指令参数是否能在常量池中定位到一个类的符号引用
  • 然后检查符号引用对应的类是否已被加载、解析和初始化。如果没有就执行。
  • 类加载通过后,虚拟机为新生的对象分配内存。
  • 内存分配完成后,虚拟机需要将分配到的内存空间都初始化为零值。
  • 虚拟机设置对象头信息。

内存分配的方式:

  • 指正碰撞,内存是规整的,所有用过的内存放在一边,空闲的内存放在另一边,中间放着一个指针作为分界点的指示器。
  • 空闲列表,内存不规整,虚拟机维护一个列表用来记录那些内存是可用的,分配时从中找到足够的内存划分给对象,并更新表记录。

保证线程安全的方式:

  • CAS,对分配的内存空间进行同步处理,采用CAS配上失败重试的方式保证操作的原子性。
  • 线程分配缓冲区,把内存分配的动作按照线程划分在不同的空间之中进行,即每个线程在Java堆中预先分配好内存。

对象的内存布局:

  • 对象在内存中存储的布局可以分为三块区域对象头、实例数据和对齐填充。
  • 对象头,又可以分为两部分,第一部分存储自身运行时数据,第二部分是类型指针
  • 自身运行数据主要包括哈希码、GC分代年龄、锁状态标志、线程持有的锁、偏向线程ID、偏向时间戳。
  • 类型指针即对象指向他的类元数据的指针,虚拟机通过这个指针来确定这个对象是哪个类的实例。
  • 实例数据,对象真正存储的有用的有效信息,继承父类的字段也会包含。
  • 对齐填充,对象起始地址必须是8的整数倍,对齐填充起到了占位符的作用。

对象的访问定位:

  • 使用句柄的访问方式,堆中将划分出来一块内存作为句柄池,reference中存储的就是对象的句柄地址,而句柄中包含了对象实例数据与类型数据各自的具体地址。reference中存储的是稳定的句柄地址,如果对象被移动,reference的地址无需改变。
  • 使用直接指针访问,reference中存储的直接就是对象的地址。直接指针访问可以减少指针定位的时间开销。

3、OOM异常

  • Java堆溢出,Java堆用于存储对象实例,只要不断的创建对象,并且保证GC Root到对象之间有可达路径来避免垃圾回收,那么对象数量到达最大堆的容量限制后就会产生内存溢出异常。

    1. package com.ecut.exception;
    2.  
    3. import java.util.ArrayList;
    4. import java.util.List;
    5.  
    6. /**
    7. * -Xms20m -Xmx20m -XX:+HeapDumpOnOutOfMemoryError
    8. */
    9. public class HeapOOM {
    10.  
    11. static class OOMObject{
    12.  
    13. }
    14. public static void main(String[] args) {
    15. List<OOMObject> list = new ArrayList<>();
    16. while (true){
    17. list.add(new OOMObject());
    18. }
    19. }
    20. }

    运行结果如下:

    1. java.lang.OutOfMemoryError: Java heap space
    2. Dumping heap to java_pid6776.hprof ...
    3. Heap dump file created [28247587 bytes in 0.149 secs]
    4. Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
    5. at java.util.Arrays.copyOf(Arrays.java:3210)
    6. at java.util.Arrays.copyOf(Arrays.java:3181)
    7. at java.util.ArrayList.grow(ArrayList.java:261)
    8. at java.util.ArrayList.ensureExplicitCapacity(ArrayList.java:235)
    9. at java.util.ArrayList.ensureCapacityInternal(ArrayList.java:227)
    10. at java.util.ArrayList.add(ArrayList.java:458)
    11. at com.ecut.exception.HeapOOM.main(HeapOOM.java:17)
  • 虚拟机栈和本地方法栈溢出,如果线程请求的栈深度大于虚拟机所允许的最大深度,将抛出StackOverflowError异常,如果虚拟机在拓展栈时无法申请到足够的内存空间则抛出OutOfMemoryError异常。
    1. package com.ecut.exception;
    2.  
    3. /**
    4. * -Xss128k
    5. */
    6. public class JavaVMStackSOF {
    7.  
    8. private int stackLength = 1;
    9.  
    10. public void stackLeak(){
    11. stackLength++;
    12. stackLeak();
    13. }
    14.  
    15. public static void main(String[] args) {
    16. JavaVMStackSOF javaVMStackSOF = new JavaVMStackSOF();
    17. try {
    18. javaVMStackSOF.stackLeak();
    19. }catch (Exception e){
    20. System.out.println("stack length :" + javaVMStackSOF.stackLength);
    21. throw e;
    22.  
    23. }
    24. }
    25. }

    运行结果如下:

    1. Exception in thread "main" java.lang.StackOverflowError
    2. at com.ecut.exception.JavaVMStackSOF.stackLeak(JavaVMStackSOF.java:12)

    创建线程导致内存溢出异常:

    1. package com.ecut.exception;
    2.  
    3. /**
    4. * -Xss2M
    5. */
    6. public class JavaVMStackOOM {
    7.  
    8. private void dontStop(){
    9. while(true){
    10.  
    11. }
    12. }
    13.  
    14. public void stackLeakByThread(){
    15. while (true){
    16. Thread thread = new Thread(new Runnable() {
    17. @Override
    18. public void run() {
    19. dontStop();
    20. }
    21. });
    22. thread.start();
    23. }
    24. }
    25.  
    26. public static void main(String[] args) {
    27. JavaVMStackOOM javaVMStackOOM = new JavaVMStackOOM();
    28. javaVMStackOOM.stackLeakByThread();
    29. }
    30. }
  • 方法区和运行时常量池溢出
    1. package com.ecut.exception;
    2.  
    3. import java.util.ArrayList;
    4. import java.util.List;
    5.  
    6. /**
    7. * -XX:PermSize=10M -XX:MaxPermSize=10M JDK 1.6 会抛出OOM异常,JDK1.7开始了去永久代
    8. */
    9. public class RuntimeConstantPoolOOM {
    10. public static void main(String[] args) {
    11. //使用list保持着产量池的引用,避免fullGC回收常量池的行为
    12. List<String> list = new ArrayList<>();
    13. int i = 0 ;
    14. while(true){
    15. list.add(String.valueOf(i++));
    16. }
    17. }
    18. }

    String.intern()方法如果字符串常量池中已经包含了一个等于String对象的字符串,则返回代表池中这个字符串的String对象。否则将此对象包含的字符串添加到常量池中。

    1. package com.ecut.exception;
    2.  
    3. public class RuntimeConstantPool {
    4. public static void main(String[] args) {
    5. /*jdk1.6 intern方法会把首次遇到的字符串实例复制到永久代中,返回的也是这个永久代中的这个字符串实例的引用。
    6. StringBuilder创建的字符串实例在Java堆上,所以必然不是同一个引用
    7. jdk1.7中intern实现只是在常量池中记录首次出现的实例引用,因此intern返回的引用和StringBuilder创建的那个字符
    8. 串实例时同一个*/
    9. String s1 = new StringBuilder("计算机").append("软件").toString();
    10. System.out.println(s1.intern() == s1);
    11. }
    12. }

    运行结果:

    1. true
  • 本机直接内存溢出
    1. package com.ecut.exception;
    2.  
    3. import sun.misc.Unsafe;
    4.  
    5. import java.lang.reflect.Field;
    6.  
    7. /**
    8. * -Xmx20M -XX:MaxDirectMemorySize = 10M
    9. */
    10. public class DirectMemoryOOM {
    11. private static final int _1MB = 1024*1024;
    12.  
    13. public static void main(String[] args) throws IllegalAccessException {
    14. Field unsafeField = Unsafe.class.getDeclaredFields()[0];
    15. unsafeField.setAccessible(true);
    16. Unsafe unsafe = (Unsafe) unsafeField.get(null);
    17. while (true){
    18. unsafe.allocateMemory(_1MB);
    19. }
    20. }
    21. }

    运行结果如下:

    1. Exception in thread "main" java.lang.OutOfMemoryError
    2. at sun.misc.Unsafe.allocateMemory(Native Method)
    3. at com.ecut.exception.DirectMemoryOOM.main(DirectMemoryOOM.java:18)

源码地址:

https://github.com/SaberZheng/jvm-test

转载请于明显处标明出处:

https://www.cnblogs.com/AmyZheng/p/10504443.html

《深入理解Java虚拟机》读书笔记一的更多相关文章

  1. 【鸟哥的Linux私房菜】笔记1

    Linux是什么 从操作系统与cpu架构关系到linux  Richard Mathew Stallman GPL 关于GNU计划 Linux的发展 Linux的核心版本 Linux的特色 Linux ...

  2. 【鸟哥的Linux私房菜】笔记3

    正确地开机 最好不要使用root账号登陆!GNOME图形界面 View items as a list X WindowShell 文本交互界面bash是Shell的名称,Linux的默认壳程序就是b ...

  3. 【鸟哥的Linux私房菜】笔记2

    Linux的应用 学习资源整理 安装记录 >< 1.Linux的应用: 网络服务器 数据库 学术机构的高效运算任务 嵌入式系统 ... 2.挂载与磁盘分区 学习资源整理 学习 1.书上的网 ...

  4. 《鸟哥的Linux私房菜》笔记——02. 关于Linux

    Unix 历史 1969年以前:伟大的梦想--Bell, MIT 与 GE 的「Multics」系统 1969年:Ken Thompson 的小型 file server system 1973年:U ...

  5. 《鸟哥的Linux私房菜》笔记——03. 磁盘分区

    Everything is a file. 常见硬件对应于 Linux 下的文件(/dev目录下) 装置 装置在Linux内的档名 SCSI/SATA/U盘硬盘机 /dev/sd[a-p] U盘 /d ...

  6. 鸟哥的linux私房菜学习笔记 __ 命令与文件的搜寻

    连续输入两次[tab]按键就能够知道使用者有多少命令可以下达.那你知不知道这些命令的完整档名放在哪里?举例来说,ls 这个常用的命令放在哪里呢? 就透过 which 或 type 来找寻吧! 范例一: ...

  7. 【鸟哥的Linux私房菜】笔记

    操作系统核心的功能! 驱动程序与操作系统的关系 2. [计算机组成之组件] 3.CPU实际要处理的数据完全来自于主存储器,这是一个很重要的概念! 4.CPU是整个计算机系统最重要的部分,那么目前世界上 ...

  8. 《鸟哥的Linux私房菜》笔记——04. 简单命令行

    键入命令 [dmtsai@study ~]$ command [-options] parameter1 parameter2 ... 指令 選項 參數(1) 參數(2) 注意:有时也可以使用 + 放 ...

  9. 鸟哥的Linux私房菜学习笔记——文件权限与目录配置

    Linux的文件权限和目录配置 在linux中的每个用户必需属于一个组,不能独立于组外.在linux中每个文件有所有者.所在组.其它组的概念. (1)所有者 一般为文件的创建者,谁创建了该文件,就是天 ...

  10. 鸟哥的Linux私房菜学习笔记(1)

    2014/10/29 1.档案的权限管理分为三个部分: 拥有者.群组.其他 2.ls -al 命令可以看到档案的详细信息 3.档案的属性中由十个部分构成 第一个部分是档案类型 -代表档案.d代表文件夹 ...

随机推荐

  1. Auto-Encoder(自编码器)原理

    1.无监督学习 无监督学习和监督学习是机器学习的两个方向,监督学习主要适用于已经标注好的数据集(如mnist分类问题),无监督学习则是希望计算机完成复杂的标注任务,简单的解释就是——教机器自己学习,它 ...

  2. MacBook Pro安装VMware Fusion 11

    下载地址 https://www.vmware.com/cn/products/fusion/fusion-evaluation.html 序列号 TX1NF-PPVRW-A1XAX-X5PVZ-Q7 ...

  3. PTA 符号配对 —— C++

    请编写程序检查C语言源程序中下列符号是否配对:/*与 */.(与 ).[与].{与}. 输入格式: 输入为一个C语言源程序.当读到某一行中只有一个句点.和一个回车的时候,标志着输入结束.程序中需要检查 ...

  4. java-标准输入

    package InputDemo; public class InputDemo1 { public static void main(String[] args) { /* 1. java.uti ...

  5. SVN提交更新文件,抛出"svn: No such revision 27106"异常问题处理

    SVN,不管是更新或者是提交原来存在的文件,都会抛出此异常"svn: No such revision 27106",注意,是原来存在的文件,要是新增的文件,不会出现此问题. 百度 ...

  6. Java泛型(T)与通配符?

    前言:使用泛型的目的是利用Java编译机制,在编译过程中帮我们检测代码中不规范的有可能导致程序错误的代码.例如,我们都知道list容器可以持有任何类型的数据,所以我们可以把String类型和Integ ...

  7. 亚马逊云推出基于机器学习的企业搜索服务Kendra,剑指微软

    近日,在AWS re:Invent全球大会上,亚马逊发布了五项新的基于机器学习的人工智能 (AI) 服务. 这五项服务包括机器学习驱动的企业搜索.代码审核与分析.欺诈检测.医疗转录和 AI 预测的人工 ...

  8. 每天进步一点点------Modelsim添加Xilinx仿真库的详细步骤

    Modelsim,可以选型SE和XE两个版本.Modelsim XE可以直接被ISE调用,而Modelsim SE需要手动添加仿真库.但SE版和OEM版在功能和性能方面有较大差别,比如对于大家都关心的 ...

  9. 微信小程序报错TypeError: this.setData is not a function

    今天在练习小程序的时候,遇到小程序报错 对于处于小白阶段的我,遇到这种报错,真还不知道是错从何来,只有一脸蒙逼,后来通过查询,终于知道了问题所在,下面对这一问题做一记录 小程序默认中是这么写的 onL ...

  10. JS首字母进行分类合并加排序

    let array = ['fds', 'ewfg1', 'cvd', 'ew', 'qer', 'jjh', 'rth', 'asd', 'vsd', 'tteh', 'fxv']; let map ...