栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 系统运维 > 运维 > Linux

JVM你值得拥有

Linux 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

JVM你值得拥有

JVM探究

开始学习JVM之前,百度搜到这篇文章,JVM只需这一篇 第一眼看的人,只能感叹一句,tql !!!
首先思考几个问题

  • 对JVM的理解,Java8虚拟机与之前的变化

  • 什么是OOM,什么是栈溢出?怎么分析?

    OOM:

    OutOfMemoryError:如果虚拟机栈可以动态扩展,而扩展时无法申请到足够的内存。

    关于OOM的一些解释
    栈溢出:

    StackOverflowError:线程请求的栈深度大于虚拟机所允许的深度。

    如何解决栈溢出
    栈溢出的原因

  • JVM的常用调优参数?

    JVM常用的调优参数

  • 内存快照如何抓取?怎么分析Dump文件?

    内存快照:内存转储快照

    Dump文件:Java Dump文件分析

  • 谈谈JVM中,类加载器你的认识?rt-jar ext appliacation

    类加载:类加载器详解

四个基础类加载器,类加载器是什么?有哪些?怎么用?

1. JVM的位置

① 什么是JVM?

JVM(Java Virtual Machine)是 Java 虚拟机,用于运行 Java 编译后的二进制字节码,最后生成机器指令。

JVM 是 Java 能够跨平台的核心

② JDK JRE JVM 的关系

JDK :(Java Development Kit),Java 开发工具包。JDK 是整个 Java 开发的核心,集成了 JRE 和javac.exe,java.exe,jar.exe 等工具。

JRE :(Java Runtime Environment),Java 运行时环境。主要包含两个部分,JVM 的标准实现和 Java 的一些基本类库。它相对于 JVM 来说,多出来的是一部分的 Java 类库。

三者的关系是:一层层的嵌套关系。JDK>JRE>JVM

JVM的位置(这里也可以回顾下冯诺依曼体系)

2. JVM体系结构

自己画张图:

首先是运行时数据区的五个区域及各自的作用:

  1. PC寄存器(程序计数器):程序计数器是线程私有的。关于PC寄存器这篇文章的介绍简单易懂https://blog.csdn.net/qq_44892091/article/details/104077934 该内存区域是唯一一个在《Java虚拟机规范》中没有规定任何OutOfMemoryError情况的区域。
  2. new创建的实例化对象及数组,是存放在堆内存中的,用完之后靠垃圾回收机制不定期自动消除。

  3. 方法区:

Java 虚拟机中,被加载类型的信息都保存在方法区中,方法区也可以被垃圾收集

  1. 本地方法栈

区别于 Java 虚拟机栈的是,Java 虚拟机栈为虚拟机执行 Java 方法(也就是字节码)服务,而本地方法栈则为虚拟机使用到的 Native 方法服务。也会有 StackOverflowError 和 OutOfMemoryError 异常。

5 Java虚拟机栈:

与程序计数器一样,Java虚拟机栈也是线程私有的。它的生命周期与线程相同。虚拟机栈描述的是Java方法执行的内存模型:每个方法被执行的时候,Java虚拟机栈都会同步一个栈帧(stack frame)用于存储局部变量表、操作数栈、动态链接、方法出口等信息。每一个方法被调用直至执行完毕的过程,就对应着一个栈帧在虚拟机中从入栈到出栈的过程。局部变量表(8大基本数据类型和对象引用)

局部变量表:存放了编译期可知的各种基本类型(boolean、byte、char、short、int、float、long、double)、对象引用(reference 类型)和 returnAddress 类型(指向了一条字节码指令的地址)

import java.util.Scanner;

public class Demo05 {
    String name;
    int age;
    String gender;    
    public static void main(String[] args) {
        int[] arr = new int[3];
        int a = 3;
        Scanner sc = new Scanner(System.in);
        int x = sc.nextInt();
        int y = sc.nextInt();
        Demo05 demo05 = new Demo05();
        int sum = demo05.sum(x,y);
        System.out.println(sum);
        demo05.name = "tom";
        Demo05 d2 = demo05;
        d2.age = 3;
        System.out.println(d2.name + " "+ d2.age+" " + d2.gender);//tom 3 null
        System.out.println(demo05.name + " "+ demo05.age+" " + demo05.gender);//tom 3 null
        demo05 = null;
        System.out.println("after demo = null");
        System.out.println(d2.name + " "+ d2.age+" " + d2.gender);//tom 3 null
        System.out.println(demo05.name + " "+ demo05.age+" " + demo05.gender);//Exception in thread "main" java.lang.NullPointerException
        
    }
    public int sum(int a,int b){
        return a+b;
    }
}

总结:

  1. 堆中存放的是new的实例化对象和数组,用完之后Java会不定期使用垃圾清除机制不定期清除
  2. 栈中存放的是基本数据类型和局部变量。用完就释放
  3. 对象的引用也存在栈中,例如demo05就是实例化对象new Demo05()的一个引用,而它引用对象的实例 化在堆中。引用对象在栈中只存储了他对应的地址,通过引用对象进行赋值的操作,值存储在堆中
  4. 数组和实例化对象一样,数组名相当于一个对象引用。这也体现了Java的一切皆对象的思想

关于Java中堆栈中各自存哪些信息,可以简单参考一下这篇 文章:堆栈

JVM架构图:JVM架构图和GC垃圾回收(文章值得细读!!!!!)

3. 类加载器,

关于类加载器这篇文章可以看看,讲的比较基础,简单易懂:类加载器

classload

作用:加载Class文件,

类加载器的分类:

package com.test;

import java.net.URL;

public class Car {
    public int age;
//java.lang包
    public static void main(String[] args) {
        Car car1 = new Car();
        Car car2 = new Car();
        Car car3 = new Car();
        car1.age = 1;
        car2.age = 2;
        car3.age = 3;
        System.out.println("======同一个类的不同的实例化对象具有不同的hashcode()值=======");
        System.out.println(car1.hashCode());
        System.out.println(car2.hashCode());
        System.out.println(car3.hashCode());

        Class aClass1 = car1.getClass();
        Class aClass2 = car2.getClass();
        Class aClass3 = car3.getClass();
        System.out.println("======同一个类具有相同的hashcode()值=======");
        System.out.println(aClass1.hashCode());//hasCode()
        System.out.println(aClass2.hashCode());
        System.out.println(aClass3.hashCode());
        System.out.println("======获取该类对应的类加载器=======");
        ClassLoader classLoader1 = aClass1.getClassLoader();
        ClassLoader classLoader2 = aClass2.getClassLoader();
        ClassLoader classLoader3 = aClass3.getClassLoader();
//        系统类加载器(system class loader):被称为系统(也称为应用)类加载器
//        程序可以通过ClassLoader的静态方法getSystemClassLoader()来获取系统类加载器。
//        如果没有特别指定,则用户自定义的类加载器都以此类加载器作为父加载器。
//        由Java语言实现,父类加载器为ExtClassLoader。
        System.out.println(classLoader1);//AppClassLoader,,rt.jar中java.lang包中 public abstract class ClassLoader
        System.out.println(classLoader2);
        System.out.println(classLoader3);
        System.out.println("======获取该类对应的类加载器的父类=======");
//        jre1.8libext
//        扩展类加载器(extensions class loader):它负责加载JRE的扩展目录,lib/ext或者
//        由java.ext.dirs系统属性指定的目录中的JAR包的类。由Java语言实现,父类加载器为null。
        ClassLoader parent = classLoader1.getParent();//ExtClassLoader扩展类,
        System.out.println(parent);
        ClassLoader parent1 = parent.getParent();//null
//        结果为null的两种情况,1.不存在,2.Java获取不到   rt.jar中java.lang包,rt.jarjavalang 
        System.out.println(parent1);
//        根加载器
        System.out.println("=======根加载器========");
        URL[] urls = sun.misc.Launcher.getBootstrapClassPath().getURLs();
        for(URL url : urls){
            System.out.println(url.toExternalForm());
        }

    }
}

类加载器,向上委派,向下执行。双亲委派机制。APP-EXT-BOOT

类加载的步骤:

  1. 类加载器收到类加载的请求,

  2. 将这个请求向上委托给父亲去完成,一直向上委托,直到启动类加载器。

  3. 启动类加载器会检查是否能加载这个类,能加载就使用当前加载类,否则,抛出异常或者通知子类加载器进行加载

  4. 子类加载器重复步骤3

null:Java调用不到~Java底层是c和C++实现的

Java = c+±-,去掉繁琐的东西,指针,内存管理。

4. 双亲委派机制

这里可以阅读一下这篇博文 双亲委派机制

里面的这张图可以说把双亲委派机制讲的非常清楚了

5. 沙箱安全机制

这篇文章内容很多很详细看一遍可能不太容易理解

沙箱sandbox

沙箱安全机制介绍
Java安全模型的核心就是Java沙箱(sandbox)
沙箱主要限制系统资源访问,系统资源包括CPU、内存、文件系统、网络

6. native

这篇博文很容易入门理解native native

javah命令详解: javah

7. PC寄存器

跟之前在第一点写过的一样,寄存器看这篇文章入门级理解应该是够了:程序计数器

8. 方法区

java方法区的理解先码一下这篇文章

Method Area 方法区

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

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

static , final , Class , 常量池

9. 栈

栈:数据结构

程序 = 数据结构 + 算法:持续学习

程序 = 框架 + 业务逻辑 吃饭

栈:先进后出,后进先出。桶模型

队列:先进先出,后进后出(First Input First Output)

堆:堆通常是一个可以被看做一棵完全二叉树的数组对象。

关于堆,栈,队列及堆栈的知识补课可以看这篇博文:堆栈队列

为什么main()方法先执行后结束

程序一执行就将main()方法压入栈中,依次执行依次压栈,执行完依次弹出栈

栈:栈内存,主管程序的运行生命周期和线程同步,

线程结束,栈内存也就释放了。对于栈来说**不存在垃圾回收问题 **

栈:8大基本类型+对象引用+实例的方法

栈运行的原理:栈运行原理

Java中堆栈运行原理

搞清楚栈+堆+方法区三者之间 的调用关系

画出一个对象在内存中实例化的过程

待补充

10. 三种主流的JVM

1. Sun公司的HotSpot 是目前使用范围最广的Java虚拟机.

2. BEA公司的JRockit(原来的 Bea JRockit)电脑软件,系列产品是一个全面的Java运行时解决方案组合。

3. IBM公司的J9 VM 是一个高性能的企业级 Java 虚拟机。

查看自己的JVM版本java -version

11. 堆

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

IDEA中可以调节VM的参数以及给main()方法传递参数

类加载器读取了类文件后,一般会把什么东西放入到堆中呢?这篇文章Java的堆栈中究竟存放了些什么东西

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

  • 新生区(伊甸园区)new
  • 老年区 old
  • 永久区 perm

12. 新生区

一个类 诞生 和 成长 的地方,甚至有可能死亡

新生区分为:伊甸园区和幸存区0区和1区

伊甸园区是new出来的

幸存者区(0,1)

经过研究,99%的对象都是临时对象

13. 老年区 14. 永久区

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

一个启动类,加载了大量的第三方jar,tomcat部署了太多的应用,大量动态生成的反射类,如果这些东西不断被加载,直到内存满,就会出现OOM;

  • jdk1.6之前:永久代,常量池是在方法区
  • jdk1.7 :有永久代,但是慢慢退化了。去悠久代,常量池在堆中
  • jdk1.8之后:无永久代,常量池在元空间

GC垃圾回收,主要在伊甸园区和养老区~

假设内存满了,OOM,堆内存不够!java.lang.OutOfMemoryError:java heap space

在jdk8以后,永久存储区改名为元空间

public class Demo02 {
    public static void main(String[] args) {
        long max = Runtime.getRuntime().maxMemory();
        long total = Runtime.getRuntime().totalMemory();
        System.out.println("max"+max+"字节t"+(max/(double)1024/1024)+"MB");
        System.out.println("max"+total+"字节t"+(total>>20)+"MB");

//        -Xms1024m -Xmx1024m -XX:+PrintGCDetails
//        305664K+699392K = 1,005,056 K = 981.5 M
//        元空间逻辑上存在,物理上不存在
//        OOM
//        1.尝试扩大堆内存看结果
//        2.分析内存,看一下那个地方出现了问题(专业工具)
    }
}

import java.util.Random;
//        -Xms8m -Xmx8m -XX:+PrintGCDetails
public class Demo03 {
    public static void main(String[] args) {
        String str = "Tom & Jerry";
        while (true)
        {
            str = str  + new Random().nextInt(999999999) + new Random().nextInt(999999999);
        }
    }
}

元空间:逻辑上存在于堆中,物理上不存在。
在一个项目中,突然出现OOM故障,那么该如何排除,究竟为什么出错

  • 能够看到第几行代码出错:内存快照分析工具,MAT,Jprofile
  • Debug,一行行分析代码!

MAT,Jprofile作用

  • 分析Dump内存文件,快速定位内存泄漏。
  • 获取堆中的数据
  • 获取大的对象
import java.util.ArrayList;
//   -Xms1m -Xmx8m -XX:+HeapDumpOnOutOfMemoryError
public class Demo04 {
    Byte[] array = new Byte[1*1024*1024];

    public static void main(String[] args) {
        ArrayList list = new ArrayList<>();
        int count = 0;
        try{
            while(true){
                list.add(new Demo04());
                count  = count + 1;
            }
        }catch (Exception e){
            System.out.println(count);
            e.printStackTrace();
        }

    }
}

安装Jprofile插件以及下载Jprofile安装版并配置IDEA

用Jprofile安装版打开Dump文件

15. 堆内存调优
import java.util.ArrayList;
//   -Xms  设置初始化分配内存大小,默认 1/64
//   -Xmx  设置最大分配内存,默认 1/4
//   -XX:+PrintGCDetails  打印GC垃圾回收信息
//   -XX:+HeapDumponOutOfMemoryError  打印OOM和Dump文件
//   -Xms1m -Xmx8m -XX:+HeapDumpOnOutOfMemoryError
//   Runtime类
public class Demo04 {
    Byte[] array = new Byte[1*1024*1024];

    public static void main(String[] args) {
        ArrayList list = new ArrayList<>();
        int count = 0;
        try{
            while(true){
                list.add(new Demo04());//OOM问题所在
                count  = count + 1;
            }
        }catch (Error e){
            System.out.println("count:"+count);
            e.printStackTrace();
        }

    }
}

16. GC 垃圾回收

????在哪个区域回收??

可以手动吗?

JVM在进行GC时,并不是堆这三个区域进行统一回收,大部分时候只在新生区进行垃圾回收

  • 新生区
  • 幸存区(from , to) from 区和 to 区可以交换位置,谁空谁是to
  • 老年区

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

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-aLWn8kKT-1634807231426)(C:UsersAdministratorAppDataRoamingTyporatypora-user-imagesimage-20211018130827834.png)]

GC算法:可以阅读这2篇博文再整理

https://www.cnblogs.com/dongl961230/p/13280415.html

https://www.cnblogs.com/sunfie/p/5125283.html

https://blog.csdn.net/jisuanjiguoba/article/details/80156781

  1. 引用计数法

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zjyxnnCp-1634807231428)(C:UsersAdministratorAppDataRoamingTyporatypora-user-imagesimage-20211018130957499.png)]

  1. 复制算法,谁空谁是to

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UxfuSJZW-1634807231428)(C:UsersAdministratorAppDataRoamingTyporatypora-user-imagesimage-20211018132048904.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-rJsRai4n-1634807231429)(C:UsersAdministratorAppDataRoamingTyporatypora-user-imagesimage-20211018132325231.png)]

  • 好处:没有内存碎片
  • 坏处:浪费内存空间,多了一半空间永远是空的(幸存区to)。假设对象100%存活(极端情况)

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

  1. 压缩清除、压缩算法

    标记清除

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vg6jyvxg-1634807231430)(C:UsersAdministratorAppDataRoamingTyporatypora-user-imagesimage-20211018141548872.png)]

优点:不需要额外的空间!

缺点:两次扫描严重浪费时间,会产生内存碎片

标记压缩

再优化

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LJlSetcq-1634807231431)(C:UsersAdministratorAppDataRoamingTyporatypora-user-imagesimage-20211018141902582.png)]

标记清除压缩

多次清除一次压缩

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

最优算法:没有最优的算法,只有最合适的算法。GC也被称为分代收集算法

年轻代

  • 存活率低
  • 复制算法!

老年代

  • 区域大,存活率高
  • 标记清除(内存碎片不是太多)+标记压缩混合 实现

要深究JVM,花时间自学,看面试题以及经典书籍《深入理解JVM虚拟机》

这篇文章有点老,可以看一看 https://www.cnblogs.com/yjd_hycf_space/p/7544768.html

18. JMM

https://blog.csdn.net/zjcjava/article/details/78406330

百度,思维导图,参考别人

单点登录-SSO架构师

VM CentOS 7

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/338592.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号