深入理解JVM的内存区域-0719
课前回顾
-
jdk-jre-jvm
-
jvm是一个假设在操作系统之上的微型操作系统,需要依赖java自带的类库
-
跨语言、跨平台
-
java虚拟机主要是一种规范,主要有hotspot、j9、taobaovm、zing(土豪才能用)
-
从内存结构开始发散学习
-
线程私有区包括虚拟机栈、本地方法栈、程序计数器
-
虚拟机栈默认大小1M
-
运行java方法所需要的的数据指令存储在栈帧中
-
操作数栈存在的意义?类似于操作系统的缓存作用
-
直接内存,unsafe在1.9中计划删除
1. jvm内存处理全流程
1.1 jvm申请内存
-
虚拟机参数设置(https://docs.oracle.com/javase/8/docs/technotes/tools/unix/java.html)
- -Xms30m(堆的初始大小) -Xmx30m(堆的最大大小) -XX:MaxMetaspaceSize=30m(元空间的最大大小)
-
方法区和堆与内存无关,jvm启动就运行起来了
1.2初始化运行时数据区
1.3类加载
- 方法区final变量、static变量、对象的类信息、
1.4执行方法
- 执行java方法,栈帧入栈
- new 对象,对象在堆里面创建,在栈帧里面会有一个该对象的引用(List l = new List<>();中的l)
1.5创建对象
2.从底层深入理解运行时数据区
2.1堆空间分代划分
eden、from、to属于新生代,tenured属于老年代
- eden(所有的对象优先在eden区分配)
- From
- To
- Tenured(老年代)
2.2 gc概念
-
-XX:+UseConMarkSweepGC -XX:-UseCompressedOops
- 打开cms垃圾回收器,关闭压缩指针命令
-
System.gc()垃圾回收主动执行,不推荐做
- 一个对象主动gc后会进入from区,回收不掉会进入to区,又返回from区,进过15次操作后,即操作年龄,就会进入老年代
2.3 JHSDB工具
-
可视化,映射JVM的运行信息
-
sawindbg.dll
-
java -cp .\sa-jdi.jar sun.jvm.hotspot.HSDB
-
attach(添加) 通过jps把需要监控的进程号输入
- Moniter ctrl-break
- surrogate
- main
- listener
-
show the momery from the thread(栈所在区域)
-
展示的是栈的使用情况
- 上面的当前栈帧
- kind:native会展示类型
- 下面一个是前一个栈帧
- Interptrted fram(蓝绿色,栈帧)
- intertreted expression (黑色的,操作数栈)
- interpteted locals area(局部变量表)
- interptrted constans pool(橙色,常量池)
- 上面的当前栈帧
-
-
-
tools
- object historims
- char
- String
- …
- 在tools里面搜索自己的包名称,可以找到对象在堆里面的信息
- address
- inspect进入对象‘
- 操作系统上一个真实的物理地址
- oop
- class description
- address
- heap paramrter(对象所在区域)
- gen 0:
- eden、from、to、的起始地址(jvm划出来的虚拟地址)
- gen 1:
- old的起始地址
- gen 0:
- object historims
-
2.4 代码改造
- system.gc15次
- thread.sleep(Integer.max_value)
2.5 JHSDB使用
- 查看栈 show the momery from the thread(栈所在区域)
- 查看堆 heap paramrter(对象所在区域)
- HSDB这个工具只有hotspot
3 虚拟机优化技术
3.1栈的优化技术
- 调用方法的栈帧和被调用方法的栈帧 ,如果调用时有传入参数,这部分的栈空间是共享的,即调用方的操作数栈是被调用方的局部方法表。
4 深入辨析堆和栈
4.1 栈
- 存储内容-线程运行时的内容,运行完毕即释放
- 与线程的关系:线程独有,对单个线程可见,对其他线程不可见
- 空间大小:默认1m,同时跑100个线程才消耗100m,实际循环时100个线程已经很多了
4.2 堆
-
存储内容:对象(new出来的)
-
线程可见,可以被所有的线程访问
-
空间大小:相比栈要大
4.3 内存溢出(oom)
机器:2g 堆:1g 方法区:800m
机器剩余 200m
同时跑200线程 可运行
当跑201个线程时,机器死机
- 让堆溢出
- -Xms30m -Xmx30m -XX:+printGCDetails
- new String[35 * 1000 * 100] 35m的数组
- 直接就会 gc failure
- oom -> java heap space
- 不断地往list加对象
- 不断触发垃圾回收
- oom -> gc overhead limit exceeded(gc占据98%的资源,回收不足2%)
- -Xms30m -Xmx30m -XX:+printGCDetails
- 方法区溢出
- 方法区存储的是静态变量、常量、.class文件
- 怎么不断往方法区里面塞.class文件
- cglib包
- while(true) Enhancer.create
- -XX:+Metaspacesize = 10m -XX:+MaxMetaspacesize = 10m
- oom : Metaspace
- cglib包
- 本地直接内存溢出
- ByteBuffer bb = ByteBuffer.allocateDirect(128 * 1000 * 1000)
- -XX:MaxDirectMemeorySize = 100m
- OOM : direct buffer memeory
- 或者使用unsafe,但是不受控制
- oom要重点关注是哪个区域抛出的
4.4 常量池(方法区)
常量池是虚拟机规范中定义的;
-
class常量池
- javap -v xxx 可以生成一个class常量池
- 存放的是编译时生成的字面量、符号引用
- String a = “b”,"b"是a的字面量
- 符号引用
- Person类引用Tools类
- 编译时不知道tools真实内存地址 Org.king.Tools — 符号引用
- 类加载时就会把符号引用变成直接引用
- Person类引用Tools类
-
运行时常量池
- 直接引用 -> 运行时常量池
- jdk1.7以后可以放在堆上,逻辑上还是在方法区上,更适合做垃圾回收
- 直接引用 -> 运行时常量池
-
字符串常量池
-
String类分析(final)
- final char value[]数组;Stirng对象的不可变
- 可变就不安全,可能被恶意修改
- hash值也不会轻易改变,可以确保hashmap key-value的稳定
- 可以实现字符串常量池
- int hash;
- final char value[]数组;Stirng对象的不可变
-
String的创建方式
-
String str = “abc”;
- 编译时,会在常量池中创建常量,不会重复创建
- 运行时,直接返回相关引用
-
String str = new String(”abc“)
- 编译时,会在常量池中创建常量“abc”
- new方法,会在堆空间创建String对象,并引用常量池中char数组,
-
Location location = new Location();
location.setCity(“深圳”);
- 直接在堆里面,不会在常量池中创建
-
String str = “ab” +“cd"+“ef”
- 3个对象,效率最低 java -> class -> java
- 编译器会自动优化成“abcded"
-
String str = “abvdef”;
for(int i = 0;i<1000;i++)
str = str + i;
- 编译器会优化出StringBuilder
- str = (new StringBuilder(String.valueof(Str)).append(i).toString());
-
String str = new String(“king”).intern();
String str2 = new String(“king”).inter();
- 在字符串常量池中去检查,有直接返回,没有则在字符串常量池中进行创建
- a==b 输出true(这里需要认为是对象)
- 推特公司优化内存,“深圳”、“北京“相同的字符串常量
-
对象(堆中间的)引用(栈、方法区中的)、
-
==是比较值,当是对象,比较的是地址,equal比较的是否同一个对象
-
-
分配内存的方式
-
答疑
1.字符串常量池在哪个区 方法区
2.“abc"重复,统一放在字符串常量池
3.inter()方法 // new 对象,字符串常量池创建,在里面找,有就返回,没有就返回,第二次使用这个方法时,不会再创建对象,直接上面创建的对象
// 去字符串常量池找师傅有等于该字符串的对象(判断字面量),如果有,直接返回对象的引用
4.虚拟栈的优化
参数10是被调用方法的局部变量
传递的是引用,一样可以共享
5.字符串常量池不会被垃圾回收,垃圾回收的主要对象是堆里面的对象
6.值传递、引用传递、int缓存,需要一节课讲清楚
7.生产环境频繁gc怎么排查?需要体系化的jvm知识
8.String如果不加final,就不是常量了,就不需要字符串常量池了,如果不加,就是一个对象,就放在堆里面了