B站狂神说Java笔记-JVM快速入门篇

Posted 闲言_

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了B站狂神说Java笔记-JVM快速入门篇相关的知识,希望对你有一定的参考价值。

狂神视频地址

https://www.bilibili.com/video/BV1iJ411d7jS


JVM 常问面试题

请你谈谈你对JVM的理解? java—>class---->

Java8虚拟机和之前的变化更新?

什么是OOM 内存溢出

什么栈溢出StackOverFlowError? 怎么分析

JVM 的常用调优参数?

内存快照如何抓取,怎么分析Dump文件?知道吗

谈谈JVM中,类加载器你的认识?


1.JVM的位置


  JVM 调优百分之99都是在堆里面调优,方法区是特殊的堆。


2.JVM的体系结构


3.类加载器

  作用:加载Class 文件,~ new Student();
  类似模板,是抽象
  对象是实现,是具体的

  1. 虚拟机自带的加载器
  2. 启动类(根)加载器
  3. 扩展类加载器
  4. 应用程序加载器

只有一个模板

4.双亲委派机制

  第一步:类加载器收到类加载的请求
  第二步:将这个请求向上委托给父类加载器去完成 ,一直向上委托,直到启动类加载器(Boot)
  第三步:启动类加载器检查是否能够加载当前和这个类 ,能加载就结束,使用当前的加载器,否则,抛出异常,通知子加载器进行加载。
  第四步:重复 第三步 步骤。

  null : java调用不到 ~C 、C++
    Java = C++ : 去掉繁琐的东西,指针,内存管理。
    Java = C+±-

双亲委派机制推荐阅读这篇文章

https://blog.csdn.net/codeyanbao/article/details/82875064


5.沙箱安全机制

  没什么东西


6.Native

  凡是带了native 关键字的,说明java 的作用范围达不到了,会去调用底层C 语言的库
  凡是带了native 关键字的,会进入本地方法栈

  调用本地接口:JNI
  JNI作用:扩展Java的使用,融合不同的编程语言为Java 所用!最初C、C++
  它在内存区域中专门开辟了一块标记区域:Native Method Stack,登记native 方法
  在最终执行的时候去加载本地方法库的方法,通过JNI

  本地方法接口(JNI)Java Native Interface
  本地方法库

private native void start0();

调用其他接口:Scoket 、WebService~http


7.PC寄存器

  程序计数器:Program Counter Register
    每个线程都有一个程序计数器,是线程私有的,就是一个指针,指向方法区中的方法字节码(用来存储指向一条指令的地址,也即将要执行的指令代码),在执行引擎读取下一条指令,是一个非常小的内存空间,几乎可以忽略不计。


8.方法区

  Method Area 方法区
  方法区是被线程共享,所有字段和方法字节码,以及一些特殊方法,如构造函数,接口代码也在此定义,简单说,所有定义的方法的信息都保存在该区域,此区域属于共享空间。

  静态变量、常量、类信息(构造方法、接口定义)、运行时的常量池存在方法区中,但是,实例变量存在堆内存中,和方法区无关

方法区里面存以下内容
  1.static
  2.final
  3.Class 类模板
  4.常量池

类加载过程(面试)

  1. new 一个类的时候,先在方法区有一个类的模板
  2. 类模板完了 ,方法区还有个常量池
  3. 引用在栈内存
  4. 真实的 对象在堆内存
  5. 引用指向堆内存真实的地址

9.栈

数据结构

  程序 = 数据结构 +算法 : 持续学习~
  程序 = 框架 + 业务逻辑 : 吃饭

  栈:先进后出、后进先出

  队列:先进先出(FIFO:First Input First Output)

  喝多了吐就是栈,吃多了拉就是队列

为什么main 方法 先执行,最后结束!

栈:栈内存,主管程序的运行,生命周期与线程同步;
线程结束,栈内存也就释放,对于栈来说,不存在垃圾回收问题

一旦线程结束,栈就over了

栈:8大基本类型+对象引用

栈运行原理:栈帧

程序正在执行的方法,一定在栈的顶部

栈 + 堆 + 方法区:的一些交互关系


10.三种JVM

  1. Sun公司 HostSpot Java HotSpot™ 64-Bit Server VM (build 25.101-b13, mixed mode)
  2. BEA Jrockit
  3. IBM J9 VM

11.堆

Heap,一个JVM 只有一个堆内存,堆内存的大小是可以调节的。

类加载器读取了类文件后,一般会把什么东西放到堆中?
类的实例、方法、常量、变量~,保存我们所有引用类型的真实对象

堆内存中还要细分为三个区域:

  1. 新生区 (伊甸园区) Young/New
  2. 养老区 old
  3. 永久区 Perm

  1.GC 垃圾回收主要是在伊甸园区养老区~
  2.假设内存满了,OOM ,堆内存不够!
  3.在JDK 8以后,永久存储区改了个名字(元空间)


12.新生区、老年区

类:诞生成长的地方、甚至死亡
伊甸园区,所有的的对象都是在伊甸园区new 出来的!
幸存者区(0,1)

假设我的伊甸园区只能存储10个对象,当伊甸园区满了,就会触发一次轻GC。

这次gc有以下情况:
  有的对象可以还被引用,就幸存下来了。
  有的对象没有被引用了,就死了、没了。

  幸存的下来的对象就移动到伊甸园区。

当伊甸园区和性幸存区都满了,就会触发一次重GC
  重gc 清理一次后,能活下来的对象就进入养老区了。

(就跟一场战争一样,不断的活下来)

真理:经过研究,99%的对象都是临时对象! new

13.永久区

  1.jdk 1.6之前:永久代,常量池是在方法区
  2.jdk 1.7 :永久代, 但是慢慢的退化了,去 永久代,常量池在堆中
  3.jdk 1.8 之后:无永久代,常量池在元空间

  永久区常驻内存的,用来存放JDK自身携带的class对象,interface元数据,存储的是Java运行时的一些环境或类信息这个区域不存在垃圾回收!关闭虚拟机就会释放这个区域的内存

  OOM出现条件:一个启动器,加载了大量的第三方jar包、Tomcat部署了太多应用,大量动态生成的反射类,直到内存满。

逻辑上存在,物理上不存在。

在一个项目中,突然出现OOM 故障,那么该如何排除研究为什么出错~
  1.能够看到代码第几行出错:内存快照分析工具,Eclipse MAT,Jprofiler
  2.Dubug,一行行分析代码~

MAT,Jprofiler作用:
  1.分析Dump内存文件,快速定位内存泄漏;
  2.获得堆中的数据
  3.获得大的对象~

  idea 安装Jprofiler

  安装之后配置

  idea 配置vm 参数

-Xms20m -Xmx20m -XX:+HeapDumpOnOutOfMemoryError

  windows 客户端安装

https://www.ej-technologies.com/download/jprofiler/files

Dump 内存文件分析

分析:
  一眼就可以看成ArrayList 出现问题了

错误代码如下

byte[] array = new byte[1*1024*1024];//1mb

public static void main(String[] args) {
    List<P1> list = new ArrayList<>();
    int count = 0;

    try {
        while (true){
            list.add(new P1());
            count++;
        }
    }catch (Throwable e){
        e.printStackTrace();
        System.out.println("count==="+count);
    }
}

  通过查看线程发现,第24行代码发现问题

  生成dump文件之后会产生 java_pid21756.hprof.analysis 文件夹,可以将其删除


14.堆内存调优

没有


15.GC :垃圾回收机制

  垃圾回收的区域只有在堆里面(方法区在堆里面)

15.1垃圾回收 = GC

JVM 在进行垃圾回收(GC)时,并不是堆这三个区域统一回收。大部分时候,回收都是新生代~
  1.新生代
  2.幸存区(form,to)
  3.老年区

  GC 两种类:轻GC(普通GC),重GC (全局GC)

  轻GC 指针对 新生代 和 偶尔走一下 幸存区。

  重GC 全部清完。

题目:
  1.JVM的内存模型和分区~详细到每一个区放什么?
  2.堆里面的分区有哪些?
  3.GC的算法有哪些?
    标记清除法
    标记整理
    复制算法
    引用计数法
    怎么用的?
  4.轻GC 和 重GC 分别再什么时候发生?

引用计数法

  假设我对象A 用了 一次就给它加上1
  假设我对象B 用了 两次就给它加上2
  假设我对象C 没有使用 就是 0

  引用计数法就是给每个对象分配一个计数器

假设C 对象为 0,它就要被清除出去了

  JVM 现在一般不采用这种方式,不高效。


15.2复制算法

  每次GC 都会将 伊甸园区 活得对象 移动到 幸存区 中,如果幸存区放不下 ,就移到养老区中。
  一旦伊甸园区被GC 后,就会是空的。
  当某对象从伊甸园区 存活下来了。

  谁空谁是to
  假设这个对象还活着,它就把这个对象复制到另一个区域 要么是 form 要么是 to

  当某个对象 经历15次(默认值)GC 都还没有死的时候,就会进入养老区。


15.3图解复制算法

  假设 幸存区里面,to 是空的,form 里面有对象。

  现在要做一次垃圾回收了
  首先:伊甸园区 存活的对象往 to 里面走

  其次,form 区里面的 对象也要往 to 里面走
  每次清理完之后,伊甸园区是空的to区是空的

  经历15次GC之后,会把幸存区 里面或者的对象 移到养老区

好处:没有内存的碎片
坏事:浪费一半内存的空间:多了一半空间用于是空的。

赋值算法最佳使用场景:对象存活度较低的时候;新生区


15.4标记清除算法

扫描这些对象,对活着的对象进行标记
清除:对没有标记的对象,进行清除

优点:不需要额外的空间
缺点:两次扫描严重浪费时间,会产生内存碎片。


15.5标记压缩


15.6总结:

  内存效率:复制算法 > 标记清除算法 > 标记压缩算法(时间复杂度)
  内存整齐度:复制算法 = 标记压缩算法 > 标记清除算法
  内存利用率: 标记压缩算法 = 标记清除算法 > 复制算法

没有最优算法吗?
  没有;
  没有最好的算法,只有最合适的算法~ ---->GC: 分代收集算法
  每一代用合适的算法就好了。

年轻代: (大部分的对象都在这里都死了)
  存活率低
  复制算法!

老年代:
  存活率高,区域大
  标记清除 + 标记压缩 混合实现

JMM
java Memory Model
什么是JMM?


学习方法

  1.百度
  2.思维导图

以上是关于B站狂神说Java笔记-JVM快速入门篇的主要内容,如果未能解决你的问题,请参考以下文章

B站狂神说Java笔记-java基础语法

B站狂神说Java---记录SpringMVC学习笔记

B站狂神说Java笔记-java流程控制

B站狂神说Java笔记-数组

B站狂神说Java笔记-多线程

B站狂神说Java笔记-面向对象编程