JVM虚拟机

JVM位置

java 模拟与运算 java模拟器使用教程_类加载器

JVM体系结构

java 模拟与运算 java模拟器使用教程_类加载器_02

java 模拟与运算 java模拟器使用教程_jvm_03

类加载器

作用:加载class文件

java 模拟与运算 java模拟器使用教程_java_04

1.虚拟机自带的加载器

2.启动类(根)加载器

3.扩展类加载器

4.应用程序加载器

寻找:4 => 3 => 2 => 1

双亲委派机制

当某个类加载器需要加载某个.class文件时,它首先把这个任务委托给他的上级类加载器,递归这个操作,如果上级的类加载器没有加载,自己才会去加载这个类。

类加载器的类别

  • BootstrapClassLoader(启动类加载器)

c++编写,加载java核心库 java.*,构造ExtClassLoaderAppClassLoader。由于引导类加载器涉及到虚拟机本地实现细节,开发者无法直接获取到启动类加载器的引用,所以不允许直接通过引用进行操作

  • ExtClassLoader (标准扩展类加载器)

java编写,加载扩展库,如classpath中的jrejavax.*或者
java.ext.dir 指定位置中的类,开发者可以直接使用标准扩展类加载器。

  • AppClassLoader(系统类加载器)
java`编写,加载程序所在的目录,如`user.dir`所在的位置的`class
  • CustomClassLoader(用户自定义类加载器)

java编写,用户自定义的类加载器,可加载指定路径的class文件

  • 源码分析
protected Class<?> loadClass(String name, boolean resolve)
            throws ClassNotFoundException
    {
        synchronized (getClassLoadingLock(name)) {
            // 首先检查这个classsh是否已经加载过了
            Class<?> c = findLoadedClass(name);
            if (c == null) {
                long t0 = System.nanoTime();
                try {
                    // c==null表示没有加载,如果有父类的加载器则让父类加载器加载
                    if (parent != null) {
                        c = parent.loadClass(name, false);
                    } else {
                        //如果父类的加载器为空 则说明递归到bootStrapClassloader了
                        //bootStrapClassloader比较特殊无法通过get获取
                        c = findBootstrapClassOrNull(name);
                    }
                } catch (ClassNotFoundException e) {}
                if (c == null) {
                    //如果bootstrapClassLoader 仍然没有加载过,则递归回来,尝试自己去加载class
                    long t1 = System.nanoTime();
                    c = findClass(name);
                    sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
                    sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
                    sun.misc.PerfCounter.getFindClasses().increment();
                }
            }
            if (resolve) {
                resolveClass(c);
            }
            return c;
        }
    }

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

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

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

4.重复步骤3

java 模拟与运算 java模拟器使用教程_加载_05

java 模拟与运算 java模拟器使用教程_java 模拟与运算_06

Native

public static void main(String[] args) {
    new Thread(()->{

    },"t1").start();
}
//native:凡是带了native 关键字的方法,说明java的作用范围达不到,会去调底层的C语言的库
//进入本地方法栈
//调用本地接口 JNI
//JNI作用:扩展java,融合不同语言
//他在内存区域中专门开辟一块标记区域 Native Method Stack 等级native方法
//在最终执行的时候,通过 JNI 加载本地方法库中的方法

方法区:所有线程共享,所有定义方法的信息都在该区域。

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

一个JVM只有一个堆内存,堆内存的大小是可以调节的。
类加载器读取了类文件后,一般会把什么东西放到堆中?类,方法,常量,变量~,保存我们所有引用类型的真实对象;
堆内存中还要细分为三个区域:

  • 新生区(伊甸园区)
  • 养老区
  • 永久区

JDK1.8之前

java 模拟与运算 java模拟器使用教程_加载_07

GC 垃圾回收主要在伊甸园区和养老区,假设内存满了,出现OOM,对内存不够。

新生区

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

真相:99%的对象都是临时对象。

老年区

永久区

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

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

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

java 模拟与运算 java模拟器使用教程_类加载器_08

元空间:在堆的概念上,逻辑上存在,物理上不存在

public class Jvm03 {

    public static void main(String[] args) {
        //返回虚拟机试图使用的最大内存
        long max = Runtime.getRuntime().maxMemory();
        //返回jvm的初始化总内存
        long total = Runtime.getRuntime().totalMemory();

        System.out.println("max=" + max + "字节\t" +(max/(double)1024/1024)+"MB");
        System.out.println("max=" + total + "字节\t" +(total/(double)1024/1024)+"MB");

        //默认情况下,分配的总内存是电脑的1/4,初始化内存是1/64

        //-Xms1024m -Xmx1024m -XX:+PrintGCDetails

        //OOM:
            //1.尝试扩大内存看结果
            //2.分析内存,看一下哪个地方吃西安了问题(使用工具)
        /*  max=1029177344字节   981.5MB
            max=1029177344字节   981.5MB
            Heap
             PSYoungGen      total 305664K, used 20971K
              eden space 262144K, 8% used
              from space 43520K, 0% used
              to   space 43520K, 0% used
             ParOldGen       total 699392K, used 0K
              object space 699392K, 0% used
             Metaspace       used 3450K, capacity 4496K, committed 4864K, reserved 1056768K
              class space    used 376K, capacity 388K, committed 512K, reserved 1048576K
         */
        //305664K + 699392K = 1005056K = 981.5M

    }
}
public class Jvm02 {
    public static void main(String[] args) {
        String str = "hiohoihoihiohiohoihoihoihoihoihiohoihoihoihoih";
        while (true){
            str = str + new Random().nextInt(888888888) + new Random().nextInt(999999999);
        }
    }
    //Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
}

使用 JProfiler 工具分析OOM原因,Runtime就是调优的类

Java 对象在内存中的实例化过程

在类的实例化过程中,内存会使用到的三个区域:栈区、堆区、方法区。

  • 堆区
  • 存储的全部都是对象,每个对象包含了一个与之对应的 class 类的信息。
  • jvm 只有一个堆区(steap),它会被所有线程共享,堆中不存放基本数据类型和对象引用,它只存放对象本身。
  • 栈区:
  • 每个线程都包含一个栈区,栈中只保存基本数据类型的值和对象以及基础数据的引用。
  • 每个栈中的数据(基本数据类型和对象的引用)都是私有的,其它栈是无法进行访问的。
  • 栈分为三个部分:基本类型变量区、执行环境上下文、操作指令区(存放操作指令)。
  • 方法区
  • 又被称为静态区,它跟堆一样,被所有的线程共享,方法区包含所有的 class 信息 和 static修饰的变量。
  • 方法区中包含的都是整个程序中永远唯一的元素,如:class、static变量。

垃圾回收(GC)

JVM在进行GC时,并不是对这三个区域统一回收。大部分时候。回收都是新生代。

  • 新生代
  • 伊甸园区
  • 幸存区(form , to)
  • 老年区

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

复制算法(新生代主要)

谁空谁是 to from复制到to,保证to永远是空的,减少内存中产生碎片,经过十五次复制,还没有死亡,进入老年区

java 模拟与运算 java模拟器使用教程_jvm_09

  • 好处:没有内存碎片
  • 坏处:浪费内存空间,假设对象100%存活,适用于对象存活度不高的场景。

标记清除算法

java 模拟与运算 java模拟器使用教程_加载_10

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

标记压缩算法

java 模拟与运算 java模拟器使用教程_类加载器_11

引用计数器

总结

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

内存整齐度:复制算法=标记压缩算法>标记清除算法

内存利用率:标记压缩算法=标记清除算法>复制算法

GC:分代收集算法

新生代:复制算法,存活率低

老年代:标记清除+标记压缩 (内存碎片不是太多)

JMM

1.什么是JMM?

JMM(Java内存模型)本身是一种抽象的概念并不真实存在,它描述的是一组规则或规范,通过这组规范定义了程序中各个变量(包括实例字段,静态字段和构成数组对象的元素)的访问方式。

2.JMM是干什么的?

作用:缓存一致性协议。用于定义数据读写的规则(遵守)

JMM定义了线程工作和主内存之间的抽象关系:线程之间的共享变量存储在主内存(Main Memory)中,每个线程都有一个私有的本地内存(Local Memory)

可见性问题:

java 模拟与运算 java模拟器使用教程_java 模拟与运算_12

解决共享对象可见性这个问题: volilate

3.怎么学习?

volilate:解决问题

JMM是一种抽象的概念,并发问题。