JVM相关总结
https://www.cnblogs.com/jiangym/p/15885161.html
JVM内存模型(JMM)
根据代码画出下面的JVM内存模型

public class Math {
public static final int initData = 666;
public static User user = new User();
public int compute(){
int a = 1;
int b = 2;
int c = (a + b) * 10;
return c;
}
public static void main(String[] args) {
Math math = new Math();
math.compute();
}
}
class User{
}

字节码class文件被“类装载子系统”,加载到运行时数据区,通过“字节码执行引擎”运行方法代码。
main方法开始运行后,就会有一个线程;每有一个线程,都会开辟一个线程特有的“虚拟机栈”(最左面),
compute()和main()在虚拟机栈里都会有一个自己的栈帧,方法对应的栈帧会在方法结束时被释放。
存栈帧的顺序也是按栈的先进后出来存和销毁。嵌套调用
(左上二图)局部变量,a,b都会放到局部变量里。
操作数栈里也是栈结构,栈顶会弹出ab然后操作,乘积为30然后放入操作数栈。在字节码文件执行时有程序计数器,计数器的数也是“字节码执行引擎”来控制的
在局部变量分配c的空间,然后从操作数里放到局部变量里。
User对象是静态的,在方法区存栈帧,指向的对象在堆中。

垃圾收集是“字节码执行引擎”开辟的一个线程,会根据可达性分析,以及三色标记算法,来处理。
分代年龄是在对象头里存,对象头第一个字宽(1字宽=4字节=32bit)存markword,(32位机)前25位是hashcode,之后四位是分代标识,
所以只能最多到1111即0-15次,就要去老年代。剩下三位与synchronized有关,存偏向锁和锁标志位。
三色标记算法
是为了在每次垃圾回收时,提升扫面效率,把已经扫过的节点忽略。
把每个对象分为黑灰白,未扫过的为白,扫过但是子节点未扫全的为灰,子节点都扫全的为黑。
扫描的线程有的时候会中断,会存在一个黑指向白的问题,导致下一次扫描的时候,白色扫不到,内存泄漏。
CMS解决的办法是,把这种场景的黑变为灰,但是还不能彻底解决这个问题,且扫完之后整体再扫一遍。
G1的解决办法是单独记录黑指向白的线,后续统一处理。
线程间通信
为提高性能,编译器和处理器常常会对指令重排序。
happens -before原则
//todo
垃圾收集器
CMS(老年代 oldGC)+parNew
一般CMS设置fullGC的阈值是内存到65%,如果等到内存满了会使用Serial-old,这个stw会很长时间
回收阶段
- 初始标记
- 标记根对象直接关联的对象 短暂stw
- 并发标记
- 找出所有与GCRoot能关联的对象,同时执行 不stw
- 并发预清理(重新标记)不一定执行
- 减少5工作量
- 并发可终止的预清理 不一定执行
- 重新标记
- 用户是并发的,怕期间修改,存在stw
- 并发清除
- 并发清除垃圾, 因为并发清除,所以不进行整理。
- 并发重置
- 清除CMS上下文信息
G1
把内存划分为若干个region,每个region在同一时刻,只属于一个代。
- Eden
- survivor
- old
- humongous 存大对象,连续的region
YongGC
mixedGC
这个是设计巧妙的部分,老年代占比到阈值(一般45%),会回收所有yongRegion同时回收部分oldRegion区的。
回收阶段
- 初始标记
- 标记根对象直接关联的对象 短暂stw
- 并发标记
- 找出所有与GCRoot能关联的对象,同时执行 不stw
- 最终标记 stw
- 筛选回收
- 根据用户期望,对回收成本进行排序,制定回收计划,并选择region回收
回收过程
region放到一个回收集(类似Set);把region存活对象放到空region里,删除region ---- 复制算法
fullGC
减少rullGC的办法:
- 增加预留内存
- 更早地垃圾回收
- 增加并发阶段使用的线程数
>jdk8 一般都不用CMS了,用G1。
垃圾回收算法
- 标记-清除
- 存在内存碎片
- 标记-整理
- 复制
- 2快内存,性能好,无碎片,内存利用率低。
- 分代收集
可达性分析
对象是否有引用,可以用引用计数法和可达性分析,java使用可达性分析
引用计数法,引用+1,释放-1,来标记,存在循环引用的问题。
可达性分析:没有到gcroot的引用,即可回收。
finalize关键字
一旦垃圾回收器准备释放对象所占的内存空间, 如果对象覆盖了finalize()并且函数体内不能是空的, 就会首先调用对象的finalize(), 然后在下一次垃圾回收动作发生的时候真正收回对象所占的空间.
finalize代码块里加东西要慎重,避免内存泄漏。
最好使用try。catch。finally
类加载机制
类加载过程
类加载的时候是按需加载的
通过运行类时添加参数TraceClassLoading参数,获取类加载的时间线


1.首先读取rt的jar包
2.加载hello类
3.加载hello类的main方法里的uesr类
类加载过程(生命周期)
加载;(验证;准备;解析) => 链接;初始化;使用;卸载
- 加载:查找并加载类的二进制数据。
- (把类的.class文件的二进制数据读入内存,存放在运行时数据区的方法区;类加载的最终结果是产生 堆区中描述对应类的Class对象);
- 链接:包括验证、准备和解析三个子阶段;
- 验证:验证class文件的二进制是否符合规范
- 准备:在准备阶段就赋值了。
- 解析: 把符号引用翻译成直接引用。
- 初始化:给类中的静态变量赋予正确的初始值;
- 创建类(new、反射、克隆、反序列化)
- 使用静态方法、非静态变量
- Class.forName("ATest"); 获取描述类的Class对象;
- 类初始化顺序,是一个面试点
- 使用
- 卸载
- 场景:
- 该类所有实例都被GC
- 加载该类的ClassLoader已被GC
- 该类的Class对象没有被引用,也没有通过反射访问该类的方法。
注意:使用能在编译期能得知的final static修饰的常量,不会导致类的初始化;
public static final int a = 2*3;//编译时常量,不会导致类初始化;
public static final int a b = (int)(Math.random()*10)/10+1; // 不是编译时常量,会初始化;
子类父类初始化过程:
先对这个类进行加载和连接-> 如果有直接父类,先加载连接初始化这个父类->重复以上步骤直到所有父类初始化,初始化当前类;
(先加载连接当前类,再加载连接初始化父类,再初始化当前类)
初始化阶段
变量给真正的值。把准备阶段给的默认值替换掉。
静态代码块执行。
创建对象时执行
普通代码块执行
构造器
类加载执行顺序
- 静态常量
- 静态变量
- 静态初始化块
- 变量
- 初始化块
- 构造器
有继承关系时,父子类加载顺序
- 父类--静态变量
- 父类--静态初始化块
- 子类--静态变量
- 子类--静态初始化块
- 父类--变量
- 父类--初始化块
- 父类--构造器
- 子类--变量
- 子类--初始化块
- 子类--构造器
类加载器classLoad
在类“加载”阶段,通过一个类的全限定名来获取描述该类的二进制字节流的这个动作的“代码”,被成为“类加载器”
除了Java虚拟机自带的根类加载器以外,其余的类加载器有且只有一个父加载器;
Java虚拟机自带加载器:
- 根(Bootstrap)类加载器:没有父类加载器。负责加载虚拟机核心类。
- 扩展(Extension)类加载器:父加载器为根加载器;继承于java.lang.ClassLoader;
- 系统(System)类加载器:也称应用类加载器,父加载器为扩展类加载器;加载classpath路径下指定的类库;继承于java.lang.ClassLoader,也是自定义加载器的默认父类;
双亲委派
类加载过程中,
会先从最顶层加载器(一般是根加载器)开始往下,
先判断父类加载器能不能加载,能加载则往下传递返回加载的类;
不能加载则继续往下判断,如果都不能加载,则会抛出ClassNotFoundException异常;
加载器之间的父子关系实际上是指加载器对象之间的包装关系,而不是类之间的继承关系;
例如:

双亲委派加载例子
String类来加载;app传到Ext传到Boo,Boo发现rt下有类,就直接加载了;
ext下的类加载;app传到Ext传到Boo,Boo发现加载不了;返回下级加载器;Ext发现ext目录下有,就直接加载了;
用户定义User类加载;app传到Ext传到Boo,Boo发现加载不了;返回下级加载器;Ext发现加载不了,返回下级加载器;App加载器发现可以加载就加载了。
若其他类加载不到,则会抛ClassNotFoundException。
所以说双亲委派是对加载器来说的,总是往上层加载器抛,之前理解的不对,之前想的时子类和父类之间的加载关系。
好处
1.确保安全,避免核心类库被加载
2.避免重复加载
3.保证类的唯一性
例如自己写String类

程序会去RT的加载器加载,但是加载到之后发现main方法不存在,就会报错。
调优
执行 TOP 命令后
场景:CPU持续100%
常见原因
- 宿主机CPU超卖
- 内存问题 大量FUllGC
- 代码存在死循环
排查办法:
- 通过TOP命令可以查看到各个进程CPU,记录其PID
- 通过Jstat命令 打出垃圾收集日志,排查是否是内存溢出:
- Jstat -gcutil 12345 5000
- 查看YGC和FGC的次数和耗时。
- 如果是内存溢出,排查内存溢出位置。
- Jmap -dump :format=b,file = ../../heap.bin PID
- 用mat等工具查看
- 如果不是内存溢出,则有可能是存在死循环。排查死循环位置。
- Top -P 【pid】 -H
- 查看CPU占比高的子线程 1234
- 转成16进制 printf "%x/n" 1234 = abc
- 打印进程堆栈 查看对应abc的具体报错
- sudo -u admin/opt/java/bin/jstack 2066 > a.txt
cat proc/cupinfo 查看CPU的核数
负载高
CPU没满,但负载比较高
等待磁盘IO的进程过多,进程队列过长。
常见场景:
- 磁盘读写请求多
- Mysql有不走索引的语句或死锁
内存异常
mem是内存使用情况
内存飙升常见场景:
- 内存溢出(堆、方法区、栈)
- 堆,fullGC也不起作用了
- 方法区:类加载过多
- 线程栈:递归太多,层级太深。
- 内存泄漏
- jmap -dump format=b file=heap.dump PID
- 用mat等分析
ThreadLocal 相关
java引用分为四种。强软弱虚四种引用。
强引用:
new 一个对象 是强引用
finalize()方法基本上不能重写,避免内存泄漏, 当一个对象被垃圾回收器clear的时候,会调用finalize
重写是为了跟踪回收过程。
System.gc() 建议gc,hotspots 会去回收
软引用:
SoftReference<> m = new SoftReference<>(new byte[1024*1024*10]);
加粗的部分为软引用
byte[] b = new byte[1024*1024*12];
这个是强引用,当执行这句话,空间不够时,上面的软引用会直接回收。
软引用适合缓存的使用,例如缓存图片。
弱引用:
遇到gc就被回收
虚引用:
NIO的零拷贝 这种情况 分配的直接内存 在JVM之外 不能直接被垃圾回收器回收
垃圾回收器里存个虚引用,回收时候有个队列,如果有相关需要删除的直接内存,则再释放。
ThreadLocal
static ThreadLocal<Person> tl = new TheadLocal<>();
现象:
一个线程set一个对象到tl里,另一个线程获取不到
原因要从tl.set()的源码入手:
set是放到当前线程的threadLocalMap里了,所以本质是thread,person对象的一个键值对,放到当前线程的map里。
所以另一个线程获取不到上一个线程存放的数据。key不同。
应用:
spring的@transactional注解中,若有两个dao层M1和M2,为了保证M1获取的数据库链接和M相同,
吧M1的connection链接放到ThreadLocal里,M2从ThreadLocal里取,这样就保证了两次连接再一个事务里。
线程池里的线程如果用到ThreadLocal了,必须要手动清理掉,防止内存泄漏
threadLocalMap的key最初用的强引用,一直也回收不掉,越积累越多,后来编程弱引用了。
用不到当前threadLocal了,要调用tl.remove(); 底层是threadLocalMap.remove(当前线程);
相关资料
https://www.bilibili.com/video/BV14K411F7S4?p=1
https://www.bilibili.com/video/BV12b4y167Mb?p=2
https://www.bilibili.com/video/BV12b4y167Mb?p=12
https://www.bilibili.com/video/BV1s44y167Yj?from=search&seid=18316383145675461585&spm_id_from=333.337.0.0
《Java面象对象编程》
JVM相关总结的更多相关文章
- JVM相关参数配置和问题诊断<转>
原文连接:http://blog.csdn.net/chjttony/article/details/6240457 1.Websphere JVM相关问题诊断: 由JVM引起的Websphere问题 ...
- 3.1日 重温JVM相关信息
1.JDK.JRE.JVM的关系: JDK是java开发的必备工具箱,JDK其中有一部分是JRE,JRE是JAVA运行环境,JVM则是JRE最核心的部分. 2.JVM的组成: JVM由4大部分组成:C ...
- JVM相关知识
Java虚拟机学习分享最近主要在学习JVM相关知识,-知识主要来源<深入理解JAVA虚拟机>,深有感触,结合自己的理解,整理出一些经验,由于篇幅较长,就把链接帖出来,希望对大家有所帮助: ...
- 了解java虚拟机—JVM相关参数设置(2)
1. JVM相关参数设置 JVM相关配置 -XX:+PrintGC 两次次YoungGC,两次FullGC. -XX:+PrintGCDetails 打印GC时的内存,并且在程序结束时打印堆内存使 ...
- 几个与JVM相关的JDK工具:jps, jstat, jmap
在项目中遇到OOM(Out of Memory)的问题,为了分析内存和JVM的垃圾回收器GC问题,一并把JVM相关的一些工具也研究了一下: jps:Java进程查看工具,实际上它和Unix/Linux ...
- Java虚拟机JVM相关知识整理
Java虚拟机JVM的作用: Java源文件(.java)通过编译器编译成.class文件,.class文件通过JVM中的解释器解释成特定机器上的机器代码,从而实现Java语言的跨平台. JVM的体系 ...
- JVM相关参数设置
Java启动参数共分为三类: 其一是标准参数(-),所有的JVM实现都必须实现这些参数的功能,而且向后兼容: 其二是非标准参数(-X),默认jvm实现这些参数的功能,但是并不保证所有jvm实现都满足, ...
- JVM相关 - 深入理解 System.gc()
本文基于 Java 17-ea,但是相关设计在 Java 11 之后是大致一样的 我们经常在面试中询问 System.gc() 究竟会不会立刻触发 Full GC,网上也有很多人给出了答案,但是这些答 ...
- JVM相关问答
大部分内容来源网络,整理一下,留个底. 问:堆和栈有什么区别? 答:堆是存放对象的,但是对象内的临时变量是存在栈内存中,如例子中的methodVar是在运行期存放到栈中的. 栈是跟随线程的,有线程就有 ...
- JVM相关知识(1)
1.JVM内存管理的机制 内存空间划分为:Sun JDK在实现时遵照JVM规范,将内存空间划分为堆.JVM方法栈.方法区.本地方法栈.PC寄存器. 堆: 堆用于存储对象实例及数组值,可以认为Java中 ...
随机推荐
- 【C++ 数据结构:链表】二刷LeetCode707设计链表
[C++链表] 使用c++重新写一遍LeetCode707设计链表 目的是熟悉c++中链表的操作 知识点 C++链表节点的实现 在c++中,一般通过结构体来定义链表的节点,也需要写构造函数(使用初始化 ...
- Linux CentOS7查看软件包安装时间
rpm -qi 软件包名,比如httpd,lrzsz [devops@host9 ~]$ rpm -qi lrzsz Name : lrzsz Version : 0.12.20 Release : ...
- 使用 flex布局 制作携程网首页
1. 技术选型 2. 搭建相关文件夹结构 3. 引入视口标签以及初始化样式 4. 常用初始化样式 5. 首页布局分析以及搜索模块布局 index.css /*搜索模块*/ .search-index{ ...
- 剑指 Offer 32 - I. 从上到下打印二叉树(java解题)
目录 1. 题目 2. 解题思路 3. 数据类型功能函数总结 4. java代码 1. 题目 从上到下打印出二叉树的每个节点,同一层的节点按照从左到右的顺序打印. 例如: 给定二叉树: [3,9, ...
- P33_小程序的页面配置
页面配置 页面配置文件的作用 小程序中,每个页面都有自己的 .json 配置文件,用来对当前页面的窗口外观.页面效果等进行配置. 页面配置和全局配置的关系 小程序中,app.json 中的 windo ...
- C# 委托原理刨析,外加和事件对比
什么是委托 委托是一种引用类型,表示对具有特定参数列表和返回类型的方法的引用. 在实例化委托时,你可以将其实例与任何具有兼容参数和返回类型的方法进行绑定. 你可以通过委托实例调用方法. 简单的理解,委 ...
- git文件管理
一.概念: git:分布式的版本管理工具Gitee(码云):是开源中国社区推出的代码托管协作开发平台,支持Git和SVN,提供免费的私有仓库托管.Gitee专为开发者提供稳定.高效.安全的云端软件开发 ...
- Centos7搭建hadoop3.3.4分布式集群
目录 1.背景 2.集群规划 2.1 hdfs集群规划 2.2 yarn集群规划 3.集群搭建步骤 3.1 安装JDK 3.2 修改主机名和host映射 3.3 配置时间同步 3.4 关闭防火墙 3. ...
- vue中央事件
详情请看这个链接https://blog.csdn.net/sinat_17775997/article/details/59025563
- swiper弹出窗口居中效果css
position: absolute; width: 800px; left: 50%; top: 50%; transform: translate(-50%, -50%);