JVM学习笔记整理

JVM

  • 请你谈谈你对jvm的理解?java8虚拟机和之前的变化更新?
  • 什么是OOM,什么是StackOverFlowError?怎么分析?
  • jvm的常用调优参数有哪些?
  • 内存快照如何抓取,怎么分析Dump文件
  • 谈谈JVM,类加载器你的认识?

JVM的位置

在这里插入图片描述

JVM的体系结构

在这里插入图片描述
在这里插入图片描述

类加载器

作用:加载class文件【类是模板,而对象是具体的】

在这里插入图片描述

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

沙箱安全机制

Native,本地方法JNI,调用本地方法,可调用C,C++

凡是带了native 关键字的,说明java的作用范围达不到,会去调用底层C语言库!

会进入本地方法栈,调用本地方法

JNI作用:扩展Java使用,融合不同的编程语言为Java所用!最初:C/C++

Java诞生的时候,C、C++横行,想要立足,必须要调用C、C++的程序

它在内存区域中专门开辟了一块标记区域:Native Method Stack,等级native方法

在最终执行的时候,加载本地方法库中的方法通过JNI

PC寄存器

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

扫描二维码关注公众号,回复: 15813906 查看本文章

方法区

Method Area方法区

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

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

:static final Class 常量池

先进后出,;类似:桶,main方法先执行最后结束~

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

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

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

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

一旦线程技术

三种JVM(了解)

堆(Heap)

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

类加载器读取了类文件后,一般会把什么东西放到堆中?类、方法、常量变量

在这里插入图片描述

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

假设内存满了,OOM,堆内存不够!

  • 新生区(伊甸园区)Young/New

    • 类:诞生 -> 和成长的地方,甚至死亡;

    • 伊甸园,所有的对象都在伊甸园区new出来的

    • 幸存者区(0,1)

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

  • 老年区 old

  • 永久区(元空间)

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

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

在这里插入图片描述

元空间,逻辑上存在,物理上不存在!

package com.tang;

public class Demo02 {
    
    
    public static void main(String[] args) {
    
    
        //返回虚拟机视图使用的最大内存
        long maxMemory = Runtime.getRuntime().maxMemory(); //字节1024*1024

        //返回jvm的总内存
        long totalMemory = Runtime.getRuntime().totalMemory();

        System.out.println("max->"+maxMemory+"字节\t"+ (maxMemory/(double)1024/1024));
        System.out.println("max->"+totalMemory+"字节\t"+ (totalMemory/(double)1024/1024));
//        System.out.println(totalMemory);
        //默认情况下:分配的总内存是电脑内存的 1/4, 而初始化的内存 :1/64
        //OOM,
            //1. 尝试调大堆内存空间
            //2.分析内存,看一下哪个地方出现问题(专业工具)

        //-Xms1024m -Xmx1024m -XX:+PrintGCDetails
        //Young  + old = totalMemory
        // 305664K + 699392K =1,005,056k = 981.5M
    }

}

max->1029177344字节	981.5
max->1029177344字节	981.5
Heap
 PSYoungGen      total 305664K, used 15729K [0x00000000eab00000, 0x0000000100000000, 0x0000000100000000)
  eden space 262144K, 6% used [0x00000000eab00000,0x00000000eba5c420,0x00000000fab00000)
  from space 43520K, 0% used [0x00000000fd580000,0x00000000fd580000,0x0000000100000000)
  to   space 43520K, 0% used [0x00000000fab00000,0x00000000fab00000,0x00000000fd580000)
 ParOldGen       total 699392K, used 0K [0x00000000c0000000, 0x00000000eab00000, 0x00000000eab00000)
  object space 699392K, 0% used [0x00000000c0000000,0x00000000c0000000,0x00000000eab00000)
 Metaspace       used 3391K, capacity 4496K, committed 4864K, reserved 1056768K
  class space    used 359K, capacity 388K, committed 512K, reserved 1048576K

Process finished with exit code 0

package com.tang;


import java.util.Random;

//-Xms8m -Xmx8m -XX:+PrintGCDetails
public class Hello {
    public static void main(String[] args) {
        String str = "duxiaoweiTestjava";
        while (true){
            str += str + new Random().nextInt(88888888)+new Random().nextInt(999999999);
        }
    }
}

[GC (Allocation Failure) [PSYoungGen: 1522K->504K(2048K)] 1522K->867K(7680K), 0.0191425 secs] [Times: user=0.00 sys=0.00, real=0.03 secs] 
[GC (Allocation Failure) [PSYoungGen: 2040K->496K(2048K)] 2403K->1187K(7680K), 0.0005133 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[GC (Allocation Failure) [PSYoungGen: 2032K->504K(2048K)] 2723K->1480K(7680K), 0.0005598 secs] [Times: user=0.19 sys=0.00, real=0.00 secs] 
[GC (Allocation Failure) [PSYoungGen: 1851K->504K(2048K)] 2828K->1800K(7680K), 0.0004825 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[GC (Allocation Failure) [PSYoungGen: 1879K->504K(2048K)] 3175K->2545K(7680K), 0.0005259 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[GC (Allocation Failure) [PSYoungGen: 1635K->504K(2048K)] 4740K->3923K(7680K), 0.0003040 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[GC (Allocation Failure) [PSYoungGen: 1637K->416K(2048K)] 6120K->5763K(7680K), 0.0006296 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[Full GC (Ergonomics) [PSYoungGen: 416K->0K(2048K)] [ParOldGen: 5347K->2810K(5632K)] 5763K->2810K(7680K), [Metaspace: 3378K->3378K(1056768K)], 0.0034702 secs] [Times: user=0.02 sys=0.00, real=0.00 secs] 
[Full GC (Ergonomics) [PSYoungGen: 1160K->0K(2048K)] [ParOldGen: 4938K->2452K(5632K)] 6099K->2452K(7680K), [Metaspace: 3387K->3387K(1056768K)], 0.0024263 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[GC (Allocation Failure) [PSYoungGen: 1121K->192K(2048K)] 5702K->4772K(7680K), 0.0004441 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[GC (Allocation Failure) [PSYoungGen: 192K->160K(2048K)] 4772K->4740K(7680K), 0.0002400 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[Full GC (Allocation Failure) [PSYoungGen: 160K->0K(2048K)] [ParOldGen: 4580K->4302K(5632K)] 4740K->4302K(7680K), [Metaspace: 3403K->3403K(1056768K)], 0.0060114 secs] [Times: user=0.00 sys=0.00, real=0.01 secs] 
[GC (Allocation Failure) [PSYoungGen: 0K->0K(2048K)] 4302K->4302K(7680K), 0.0002241 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[Full GC (Allocation Failure) [PSYoungGen: 0K->0K(2048K)] [ParOldGen: 4302K->4283K(5632K)] 4302K->4283K(7680K), [Metaspace: 3403K->3403K(1056768K)], 0.0057527 secs] [Times: user=0.17 sys=0.00, real=0.01 secs] 
Heap
 PSYoungGen      total 2048K, used 75K [0x00000000ffd80000, 0x0000000100000000, 0x0000000100000000)
  eden space 1536K, 4% used [0x00000000ffd80000,0x00000000ffd92f50,0x00000000fff00000)
  from space 512K, 0% used [0x00000000fff80000,0x00000000fff80000,0x0000000100000000)
  to   space 512K, 0% used [0x00000000fff00000,0x00000000fff00000,0x00000000fff80000)
 ParOldGen       total 5632K, used 4283K [0x00000000ff800000, 0x00000000ffd80000, 0x00000000ffd80000)
  object space 5632K, 76% used [0x00000000ff800000,0x00000000ffc2edf8,0x00000000ffd80000)
 Metaspace       used 3449K, capacity 4496K, committed 4864K, reserved 1056768K
  class space    used 367K, capacity 388K, committed 512K, reserved 1048576K
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
	at java.util.Arrays.copyOf(Arrays.java:3332)
	at java.lang.AbstractStringBuilder.ensureCapacityInternal(AbstractStringBuilder.java:124)
	at java.lang.AbstractStringBuilder.append(AbstractStringBuilder.java:674)
	at java.lang.StringBuilder.append(StringBuilder.java:208)
	at com.tang.Hello.main(Hello.java:11)

Process finished with exit code 1

堆内存调优

//-Xms 设置初始化内存大小,默认1/64
//-Xmx 设置最大分配内存,默认1/4
//-XX:+PrintGCDetails //打印GC垃圾回收信息
//-XX:+HeapDumpOnOfMemoryError //OOM DUMP
//-Xms1m -Xmx8m -XX:+HeapDumpOnOfMemoryError
// 一般首先查看大对象,然后查看线程,找到错误位置行数

jprofiler,idea安装,然后官网下载一个,添加VM options参数

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

package com.tang;

import java.util.ArrayList;

// -Xms64m -Xmx128m -XX:+HeapDumpOnOutOfMemoryError
public class Demo03Jprofiler {
    
    
    byte[] array = new byte[1*1024*1024]; //1m

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

    }
}

java.lang.OutOfMemoryError: Java heap space
Dumping heap to java_pid15024.hprof ...
Heap dump file created [123794203 bytes in 0.074 secs]
116
java.lang.OutOfMemoryError: Java heap space
	at com.tang.Demo03Jprofiler.<init>(Demo03Jprofiler.java:7)
	at com.tang.Demo03Jprofiler.main(Demo03Jprofiler.java:14)

找到文件,一般src或者根目录中

在这里插入图片描述
在这里插入图片描述

先找大对象,堆【放对象的】,然后找线程具体问题所在

GC

在这里插入图片描述

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

    • 新生代Eden
    • 幸存区(from ,to)会交换
    • 老年区

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

在这里插入图片描述

  • GC的算法有哪些?标记清除法、标记压缩,复制算法,引用计数器等

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

  • 好处:内有内存的碎片~

  • 坏处:浪费了内存空间:多了一半空间永远是to。假设对象100%存货(极端情况下)OOM

    复制算法最佳使用场景:对象存货低的时候:新生区

  • 标记算法

在这里插入图片描述

优点:不需要额外的空间

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

  • 标记压缩
    在这里插入图片描述

总结

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

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

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

年轻代:

​ - 存活率低

​ - 复制算法

老年代

​ - 区域大:存活率

​ - 标记清除(内存碎片不是太多)+标记压缩混合实现

GC -> 分代收集算法

JMM

  1. 什么是JMM?

    java内存模型

  2. 它干嘛的?:官方,其他人的博客,对应的视频

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

    JMM定义了线程工作内存和主内存之间的抽象关系,县城之间的共享变量存储在主内存中,每个线程都有一个私有的本地内存
    在这里插入图片描述

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

  3. 它该如何学习?:JMM是一个抽象的概念

反射

package com.tang;

public class test {
    
    
    public static void main(String[] args) throws ClassNotFoundException {
    
    
        //通过反射 获取类的Class对象
        Class forName = Class.forName("com.tang.User");
        System.out.println(forName);
        System.out.println(forName);

        //反射
        Class c1 = Class.forName("com.tang.User");
        Class c2 = Class.forName("com.tang.User");
        Class c3 = Class.forName("com.tang.User");
        // 正常
        User user = new User();
        Class c4 = user.getClass();
        //
        Class c5 = User.class;

        // 一个类在内存中只有一个Class对象
        //一个类被加载后,类的整个结构都会被封装在Class对象中
        System.out.println(c1.hashCode());
        System.out.println(c2.hashCode());
        System.out.println(c3.hashCode());
        System.out.println(c4.hashCode());
        System.out.println(c5.hashCode());
    }
}
//pojo
class User{
    
    
    private String name;
    private int id;
    private int age;

    public String getName() {
    
    
        return name;
    }

    public void setName(String name) {
    
    
        this.name = name;
    }

    public int getId() {
    
    
        return id;
    }

    public void setId(int id) {
    
    
        this.id = id;
    }

    public int getAge() {
    
    
        return age;
    }

    public void setAge(int age) {
    
    
        this.age = age;
    }

    @Override
    public String toString() {
    
    
        return "User{" +
                "name='" + name + '\'' +
                ", id=" + id +
                ", age=" + age +
                '}';
    }
}

通过想反射,拿到这个类

  • 一个加载的类,在JVM中只会有一个Class实例,无论创建多少个对象,这个类只有一个

  • 一个Class对象对应的是一个加载到JVM的一个.class文件

  • 每个类的实例都记得自己是由哪个Class实例所生成

  • 通过Class可以完整地得到这个类的所有被加载的结构

  • Class是Reflection的根源,对任何你想动态加载、运行的类,唯有先获得响应的Class对象

  1. Class cla = Person.class; //最安全可靠
  2. Class cla = person.getClass(); //通过具体对象获得
  3. Class cla = Class.forName(“demo01.Person”); //通过静态方法forName()获取
  4. 内置基本数据类型可以直接类名.Type
  5. 还可以利用ClassLoader

类的初始化

  1. new对象时候
  2. 反射创建对象时候
  1. 调用类的静态代码块,常量,不会初始化类
  2. 子类调用父类的静态变量,子类不会初始化,父类会初始化
  3. 先静态代码块,然后构造器,然后常量值

猜你喜欢

转载自blog.csdn.net/u013080870/article/details/128322559