《深入理解java虚拟机》读书笔记一——第二章
第二章 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到对象之间有可达路径来避免垃圾回收,那么对象数量到达最大堆的容量限制后就会产生内存溢出异常。
package com.ecut.exception; import java.util.ArrayList;
import java.util.List; /**
* -Xms20m -Xmx20m -XX:+HeapDumpOnOutOfMemoryError
*/
public class HeapOOM { static class OOMObject{ }
public static void main(String[] args) {
List<OOMObject> list = new ArrayList<>();
while (true){
list.add(new OOMObject());
}
}
}运行结果如下:
java.lang.OutOfMemoryError: Java heap space
Dumping heap to java_pid6776.hprof ...
Heap dump file created [28247587 bytes in 0.149 secs]
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
at java.util.Arrays.copyOf(Arrays.java:3210)
at java.util.Arrays.copyOf(Arrays.java:3181)
at java.util.ArrayList.grow(ArrayList.java:261)
at java.util.ArrayList.ensureExplicitCapacity(ArrayList.java:235)
at java.util.ArrayList.ensureCapacityInternal(ArrayList.java:227)
at java.util.ArrayList.add(ArrayList.java:458)
at com.ecut.exception.HeapOOM.main(HeapOOM.java:17) - 虚拟机栈和本地方法栈溢出,如果线程请求的栈深度大于虚拟机所允许的最大深度,将抛出StackOverflowError异常,如果虚拟机在拓展栈时无法申请到足够的内存空间则抛出OutOfMemoryError异常。
package com.ecut.exception; /**
* -Xss128k
*/
public class JavaVMStackSOF { private int stackLength = 1; public void stackLeak(){
stackLength++;
stackLeak();
} public static void main(String[] args) {
JavaVMStackSOF javaVMStackSOF = new JavaVMStackSOF();
try {
javaVMStackSOF.stackLeak();
}catch (Exception e){
System.out.println("stack length :" + javaVMStackSOF.stackLength);
throw e; }
}
}运行结果如下:
Exception in thread "main" java.lang.StackOverflowError
at com.ecut.exception.JavaVMStackSOF.stackLeak(JavaVMStackSOF.java:12)创建线程导致内存溢出异常:
package com.ecut.exception; /**
* -Xss2M
*/
public class JavaVMStackOOM { private void dontStop(){
while(true){ }
} public void stackLeakByThread(){
while (true){
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
dontStop();
}
});
thread.start();
}
} public static void main(String[] args) {
JavaVMStackOOM javaVMStackOOM = new JavaVMStackOOM();
javaVMStackOOM.stackLeakByThread();
}
} - 方法区和运行时常量池溢出
package com.ecut.exception; import java.util.ArrayList;
import java.util.List; /**
* -XX:PermSize=10M -XX:MaxPermSize=10M JDK 1.6 会抛出OOM异常,JDK1.7开始了去永久代
*/
public class RuntimeConstantPoolOOM {
public static void main(String[] args) {
//使用list保持着产量池的引用,避免fullGC回收常量池的行为
List<String> list = new ArrayList<>();
int i = 0 ;
while(true){
list.add(String.valueOf(i++));
}
}
}String.intern()方法如果字符串常量池中已经包含了一个等于String对象的字符串,则返回代表池中这个字符串的String对象。否则将此对象包含的字符串添加到常量池中。
package com.ecut.exception; public class RuntimeConstantPool {
public static void main(String[] args) {
/*jdk1.6 intern方法会把首次遇到的字符串实例复制到永久代中,返回的也是这个永久代中的这个字符串实例的引用。
StringBuilder创建的字符串实例在Java堆上,所以必然不是同一个引用
jdk1.7中intern实现只是在常量池中记录首次出现的实例引用,因此intern返回的引用和StringBuilder创建的那个字符
串实例时同一个*/
String s1 = new StringBuilder("计算机").append("软件").toString();
System.out.println(s1.intern() == s1);
}
}运行结果:
true
- 本机直接内存溢出
package com.ecut.exception; import sun.misc.Unsafe; import java.lang.reflect.Field; /**
* -Xmx20M -XX:MaxDirectMemorySize = 10M
*/
public class DirectMemoryOOM {
private static final int _1MB = 1024*1024; public static void main(String[] args) throws IllegalAccessException {
Field unsafeField = Unsafe.class.getDeclaredFields()[0];
unsafeField.setAccessible(true);
Unsafe unsafe = (Unsafe) unsafeField.get(null);
while (true){
unsafe.allocateMemory(_1MB);
}
}
}运行结果如下:
Exception in thread "main" java.lang.OutOfMemoryError
at sun.misc.Unsafe.allocateMemory(Native Method)
at com.ecut.exception.DirectMemoryOOM.main(DirectMemoryOOM.java:18)
源码地址:
https://github.com/SaberZheng/jvm-test
转载请于明显处标明出处:
《深入理解java虚拟机》读书笔记一——第二章的更多相关文章
- 深入理解Java虚拟机 -- 读书笔记(1):JVM运行时数据区域
深入理解Java虚拟机 -- 读书笔记:JVM运行时数据区域 本文转载:http://blog.csdn.net/jubincn/article/details/8607790 本系列为<深入理 ...
- 【Todo】深入理解Java虚拟机 读书笔记
有一个在线系列地址 <深入理解Java虚拟机:JVM高级特性与最佳实践(第2版)> http://book.2cto.com/201306/25426.html 已经下载了这本书(60多M ...
- 深入理解Java虚拟机读书笔记5----虚拟机字节码执行引擎
五 虚拟机字节码执行引擎 1 运行时栈帧结构 ---栈帧是用于支持虚拟机进行方法调用和方法执行的数据结构,是虚拟机运行时数据区中的虚拟机栈的栈元素. ---栈帧中存储了方法的局部变 ...
- 深入理解java虚拟机读书笔记--java内存区域和管理
第二章:Java内存区域和内存溢出异常 2.2运行时数据区域 运行时数据区分为方法区,堆,虚拟机栈,本地方法栈,程序计数器 方法区和堆是线程共享的区域 虚拟机栈,本地方法栈,程序计数器是数据隔离的数据 ...
- 深入理解java虚拟机---读书笔记
第一章 走近java 1. java 技术体系: java 程序设计语言 各种硬件平台上的java虚拟机 class 文件格式 java API 类库 来自商业机构或者开源机构的第三方java类库 j ...
- 深入理解Java虚拟机——读书笔记
首先 强烈推荐周志明老师的这本书,真的可以说是(起码中文出版界)新手了解Java虚拟机必须人手一本的教科书!!! 第二部分自动内存管理机制 由于Java虚拟机的多线程是通过线程轮流切换并分配处理器 ...
- 深入理解Java虚拟机读书笔记8----Java内存模型与线程
八 Java内存模型与线程 1 Java内存模型 ---主要目标:定义程序中各个变量的访问规则,即在虚拟机中将变量存储到内存和从内存中取出变量这样的底层细节. ---此处的变量和J ...
- 深入理解Java虚拟机读书笔记7----晚期(运行期)优化
七 晚期(运行期)优化 1 即时编译器(JIT编译器) ---当虚拟机发现某个方法或代码块的运行特别频繁时,就会把这些代码认定为“热点代码”,包括被多次调用的方法和被多次执行的循环体. ...
- 深入理解Java虚拟机读书笔记4----虚拟机类加载机制
四 虚拟机类加载机制 1 类加载机制 ---概念:虚拟机把描述类的数据从Class文件加载到内存,并对数据进行校验.转换解析和初始化,最终形成可以被虚拟机直接使用的Java类型. -- ...
- 深入理解Java虚拟机读书笔记3----类文件结构
三 类文件结构 1 Java虚拟机的两种中立特性 · 平台无关性 · 语言无关性 实现平台无关性和语言无关性的基础是虚拟机和字节码存储格式(Class文件). 2 Clas ...
随机推荐
- python笔记23(面向对象课程五)
今日内容 上节作业 单例模式 class Foo: pass obj1 = Foo() # 实例,对象 obj2 = Foo() # 实例,对象 日志模块(logging) 程序的目录结构 内容回顾 ...
- 有关版本控制--SVN
什么是版本控制? 这个之前有记录过相关的内容 版本管理就是管理更新的历史记录, 它给我们提供了一些在软件开发过程中必不可少的功能,例如: 记录一款软件添加或更改源代码的过程 回滚到特定阶段,恢复误删除 ...
- MySQL锁与事务隔离级别
一.概述 1.锁的定义 锁是计算机协调多个进程或线程并发访问某一资源的机制. 在数据库中,除了传统的计算资源(如CPU.RAM.IO等)的争用以外,数据也是一种供需要用户共享的资源.如何保证数据并发访 ...
- [Linux] ubuntu下yarn依赖管理工具的安装和使用
Yarn 对你的代码来说是一个包管理器, 你可以通过它使用全世界开发者的代码, 或者分享自己的代码.Yarn 做这些快捷.安全.可靠,所以你不用担心什么.通过Yarn你可以使用其他开发者针对不同问题的 ...
- .NET CORE(C#) WPF 抽屉式菜单
微信公众号:Dotnet9,网站:Dotnet9,问题或建议:请网站留言, 如果对您有所帮助:欢迎赞赏. .NET CORE(C#) WPF 抽屉式菜单 阅读导航 本文背景 代码实现 本文参考 源码 ...
- MySQL安装详细步骤(附迅雷下载链接)
环境:windows10.64bit.mysql 8.0.19 迅雷下载链接8.0版本 https://cdn.mysql.com//Downloads/MySQLInstaller/mysql-in ...
- mysql 表结构操作
alter table name : alter table table1 to table2;add column : alter table 表名 add column 列名 varchar(); ...
- Mac-App Store 购买过程中出错 请求超时
打开终端 输入下面命令回车: defaults delete com.apple.appstore.commerce Storefront 接上步骤,继续输入下面命令回车: defaults writ ...
- Phpstorm 2020-01-04试了可用的激活码【亲测可用】WebStrom
[直接点击试用30天] http://myphp.vip/ 测试时间:2018-10-12可用(v2019.2) 测试时间:2019-12-24可用(v2019.2) 测试时间:2020-01-04可 ...
- 使用scrapy框架爬取图片网全站图片(二十多万张),并打包成exe可执行文件
目标网站:https://www.mn52.com/ 本文代码已上传至git和百度网盘,链接分享在文末 网站概览 目标,使用scrapy框架抓取全部图片并分类保存到本地. 1.创建scrapy项目 s ...