JVM复盘

1. 先来了解下对象的实例和对象的应用

讲的非常好:https://www.cnblogs.com/zmy-520131499/p/11165851.html
总结下:
对象的实例既是对象本身此时并没有赋值只是一个Class文件(可以理解为一个java文件)如new Student()的Student----保存在堆中
对象的引用既Student a= new Student(),a为对象的引用,引用的什么引用的是Student的在堆中的地址,a保存在栈中。

2. JVM内存结构

先分享个文章:https://www.cnblogs.com/zmy-520131499/p/11165851.html
讲的非常好
总结下:
在这里插入图片描述
补充:堆TLAB区域,分配时候线程独有,运行时线程共享。(pc寄存器、栈{java虚拟机栈,本地方法栈})、(堆{TLAB}、方法区{运行时常量(…)})
除了堆还有一个直接内存区域

常量池概念:

$ String a  ="abc";
$ String b = "abc";
$ System.out.println(a==b);

“abc”是保存在方法区(静态变量、基本类型值,“abc”这种常量)的常量池是线程共享的数据。
固有a和b指向的是同一个变量答案为true。

String a  ="a"+"b"+"c";

只创建了一个变量:‘a’,‘b’,‘c’是常量,当这样的时候jvm会优化为“abc”,如果没有‘'abc’就创建在常量池

$ String a ="a";
$ String b ="b";
$ String c ="c";
$ String s = a+b+c; 
$ String s1 = "abc";
$ System.out.println(s==s1); //true还是false???

常量池:“a”,‘b’,‘c’,‘'abc’
堆:‘'abc’
s为堆中的abc
s1为常量池的abc
所以为false
在这里插入图片描述
在这里插入图片描述
总结:
1.常量池中存储了编译器编译后的数据,而运行时常量池的常量,基本来源于各个class文件中的常量池,可以理解为方法区的“共享”.运行时常量池是方法区的一部分,所以也是全局共享的。
2.程序运行期间,可以手动想常量池添加常量,例如调用String的intern()方法。
3.要注意区分new出来的对象是在堆中创建的
4.编译器会对常量进行优化,但编译期间无法确定变量的值

参考:https://blog.csdn.net/Mrsssswan/article/details/87938193

方法区概念:

方法区的作用是存储 Java 类的结构信息,当我们创建对象实例后,对象的类型信息存储在方法中,实例数据存放在堆中;实例数据指的是在 Java 中创建的各种实例对象,类型信息指的是定义在 Java 代码中的常量、静态变量、以及在类中声明的各种方法、方法字段等等;同时可能包括即时编译器编译后产生的代码数据。

3JVM学习目录

面试JVM通关:https://blog.csdn.net/qq_31463999/article/details/87966505
总结:内存结构,类加载过程,垃圾回收,jvm调优工具,实战调优

栈+堆+方法区的交互关系

操作数栈可以是任意的java数据类型
在这里插入图片描述
我是这么理解 int a= 5; a保存在栈,5在方法区
Student a= new Student() Student保存在堆,a保存在栈
栈可以保存任意的java类型。

1内存结构(内存模型)

上面已经介绍了
补充:

PC寄存器:

我们知道对于一个处理器(如果是多核cpu那就是一核),在一个确定的时刻都只会执行一条线程中的指令,一条线程中有多个指令,为了线程切换可以恢复到正确执行位置,每个线程都需有独立的一个程序计数器,不同线程之间的程序计数器互不影响,独立存储。

注意:如果线程执行的是个java方法,那么计数器记录虚拟机字节码指令的地址。如果为native【底层方法】,那么计数器为空。这块内存区域是虚拟机规范中唯一没有OutOfMemoryError的区域。

栈:

对象的引用(其中的一部分)

每个方法被执行的时候都会创建一个栈帧用于存储局部变量表,操作栈,动态链接,方法出口等信息。每一个方法被调用的过程就对应一个栈帧在虚拟机栈中从入栈到出栈的过程。【栈先进后出,下图栈1先进最后出来
在这里插入图片描述
在这里插入图片描述

本地方法栈:

在这里插入图片描述
本地方法栈是与虚拟机栈发挥的作用十分相似,区别是虚拟机栈执行的是Java方法(也就是字节码)服务,而本地方法栈则为虚拟机使用到的native方法服务,可能底层调用的c或者c++,我们打开jdk安装目录可以看到也有很多用c编写的文件,可能就是native方法所调用的c代码。

方法区:(线程共享的)

用于存储已被虚拟机加载的类信息、常量、静态变量,如static修饰的变量加载类的时候就被加载到方法区中
在这里插入图片描述
运行时常量池

是方法区的一部分,class文件除了有类的字段、接口、方法等描述信息之外,还有常量池用于存放编译期间生成的各种字面量和符号引用。

堆:对象存在堆中、分为新生代和老年代(线程共享的)

存放对象实例的啦MMP的

2类加载深入解析

参考:https://blog.csdn.net/qq_31463999/article/details/87931709

类加载:类加载器实际上就是将class文件加载到虚拟机的内存的过程。在这里插入图片描述
加载:在硬盘上查找并通过IO读入字节码文件
在这里插入图片描述
找到.class; 转化为jvm运行时内存结构保存在方法区 ;生成java.lang.class对象保存在堆中

连接:
1执行校验: 校验字节码文件的正确性 文件格式、字节码、等符合一些规范
2准备:给类的静态变量分配内存,并赋予默认值(后面一步才会赋值) 静态变量分配内存并赋默认初值(0值或null值)。如static int a = 100;静态变量a就会在准备阶段被赋默认值0。
3解析:类装载器装入类所引用的其他所有类 把类符号引化为直接引用。

初始化:对类的静态变量初始化为指定的值,执行静态代码块 当用到该类时就初始化。

来一个完成功能图,有不对的地方感谢指正
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
可以参考
https://blog.csdn.net/qq_31156277/article/details/80188110
类加载器种类

启动类加载器:负责加载JRE的核心类库,如jre目标下的rt.jar,charsets.jar等
扩展类加载器:负责加载JRE扩展目录ext中JAR类包
系统类加载器:负责加载ClassPath路径下的类包
用户自定义加载器:负责加载用户自定义路径下的类包
在这里插入图片描述

委派:父类加载,找不到则子类加载。修改class文件后,重启JVM才能生效(缓存机制的应用)。先从缓存找如果要使用的时候。
在这里插入图片描述
双亲委派模式:
指先委托父类加载器寻找目标类,在找不到的情况下在自己的路径中查找并载入目标类

3垃圾回收机制

问题一什么是垃圾?

答:所谓垃圾回收机制就是对一些不用的对象所占用的内存空间进行回收。那么现在有两个问题一:怎么判断它是不是垃圾(在JVM中回收java对象所占用的空间)?
两种方法:
1,引用计数方法。当JVM中有new 对象时,会分配一个内存空间给它,当有引用时计数为1。如果object ob1=new object(); object ob2=new object(); obj2=obj1;此时obj2的引用就会计数为0。此时2就是被回收的对象。优点:它效率高,快。缺点:循环引用时则不能被回收。
2,可达性分析法。通过一系列的GC-ROOTs对象作为起点进行搜索,如果GC-ROOTS和对象没有可达的路经,则对象不可达的。

问题二:如何回收(算法)?

在确定了哪些是需要被回收的之后就要做如何回收,如何高效的回收了。
1,标记清除算法:
优点:简单,容易。缺点:会有内存碎片,到时候有大的对象时不好分配。
2,复制算法:
内存分为两块,每次用一块。当要回收时候,将存在的对象复制到另一块去,然后在清除已用的内存。优点:简单,高效,没碎片内存。缺点:内存空间使用上没有合理应用。
3,标记整理法:
将存在的对象向一边移动,清除一边的对象。
4,分代收集算法:
将堆分为老、新代。老的(老-少-):标记整理算法。新的(新-多-有用的少-复制):复制算法。注意:还有一个区是永久代,存储类,方法,常量。回收废弃的常量和无用的类。

在这里插入图片描述

补充说明

方法区、运行时常量池、永久代、元空间。

1:方法区存储的是类的一些基本信息,例如常量、静态变量等。
而,
HotSpot VM把GC分代收集扩展至方法区, 即使用Java堆的永久代来实现方法区, 这样 HotSpot 的垃圾收集器就可以像管理 Java 堆一样管理这部分内存,而不必为方法区开发专门的内存管理器(永久带的内存回收的主要目标是针对常量池的回收和类型的卸载, 因此收益一般很小)。
2,运行时常量池(Runtime Constant Pool)是方法区的一部分。Class 文件中除了有类的版
本、字段、方法、接口等描述等信息外,还有一项信息是常量池。
3、永久代也是存储那些信息的。
4、元空间,
在这里插入图片描述
总结:
那么类的元数据都被保存在了本地内存中,而一些常量池、静态变量则放入了堆中的 方法区中(hotspot vm在堆中实现的逻辑内存。)

补充说明:
java.lang.class对象是保存在永久代区域的,和普通的对象保存在堆中的对象存储区域不同。那么实际上,方法区被hotspot当作堆中的永久代来实现的。并且在java8后呢,永久代被废除了,开辟了一个元空间来管理元数据,例如class对象,而元空间内存不在JVM中,保存在本地内存了,当然像常量、静态变量还是在方法区的。这样应该很能理解疑惑了吧

两个待解决:
jvm性能调优工具
实战调优

堆内存分布情况

新生代 2/3 en8 from1 to1
老年代1/3

垃圾回收器

回收过程,G1 、CMS!
参考文档。

CMS目标是获取最短垃圾回收停顿时间,采用标记清除算法。
初始标记->并发标记(和用户一起)->重新标记->标记清除(和用户一起)

何时会stopWord?
垃圾回收时的,初始标记和重新标记。
偏向锁取消时候,因为要获取所有线程的状态。

G1目标时避免FULL GC,精准控制回收时间。在这里插入图片描述
年轻代回收会stopword,但是它能精准控制,原因是,会计算每一小块的区域大小,以便知道回收时间
回收过程详解:https://blog.didispace.com/step-by-step-g1/

线上CPU100%怎么排查:

  1. 使用阿里的Arthas:查看服务,查看线程PID,定位代码
    https://www.cnblogs.com/stormlong/articles/11439110.html
  2. 使用jstack命令top pid 查看线程pid ,grep二进制的pid查看代码。
    https://www.cnblogs.com/stormlong/articles/11439110.html

线上服务突然OOM怎么排查:

  1. jmp命令查看服务的相关对象信息,每个实例的内存大小,个数等等。
  2. dump文件到本地使用jvisualVM来进行分析。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Go语言(也称为Golang)是由Google开发的一种静态强类型、编译型的编程语言。它旨在成为一门简单、高效、安全和并发的编程语言,特别适用于构建高性能的服务器和分布式系统。以下是Go语言的一些主要特点和优势: 简洁性:Go语言的语法简单直观,易于学习和使用。它避免了复杂的语法特性,如继承、重载等,转而采用组合和接口来实现代码的复用和扩展。 高性能:Go语言具有出色的性能,可以媲美C和C++。它使用静态类型系统和编译型语言的优势,能够生成高效的机器码。 并发性:Go语言内置了对并发的支持,通过轻量级的goroutine和channel机制,可以轻松实现并发编程。这使得Go语言在构建高性能的服务器和分布式系统时具有天然的优势。 安全性:Go语言具有强大的类型系统和内存管理机制,能够减少运行时错误和内存泄漏等问题。它还支持编译时检查,可以在编译阶段就发现潜在的问题。 标准库:Go语言的标准库非常丰富,包含了大量的实用功能和工具,如网络编程、文件操作、加密解密等。这使得开发者可以更加专注于业务逻辑的实现,而无需花费太多时间在底层功能的实现上。 跨平台:Go语言支持多种操作系统和平台,包括Windows、Linux、macOS等。它使用统一的构建系统(如Go Modules),可以轻松地跨平台编译和运行代码。 开源和社区支持:Go语言是开源的,具有庞大的社区支持和丰富的资源。开发者可以通过社区获取帮助、分享经验和学习资料。 总之,Go语言是一种简单、高效、安全、并发的编程语言,特别适用于构建高性能的服务器和分布式系统。如果你正在寻找一种易于学习和使用的编程语言,并且需要处理大量的并发请求和数据,那么Go语言可能是一个不错的选择。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值