初学JVM的浅层理解

如有问题欢迎大家指正,非常感谢!

带着问题学习!
JVM题目:

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

一、JVM是什么?

JVM是Java Virtual Machine(Java虚拟机)的缩写,引入Java语言虚拟机后,Java语言编译程序只需生成在Java虚拟机上运行的目标代码(字节码),就可以在多种平台上不加修改地运行。

二、JVM的体系结构

在这里插入图片描述

三、类装载器

在这里插入图片描述

public class Car {

    public static void main(String[] args) throws IllegalAccessException, InstantiationException {

        //类是模板,对象是具体的实例
        Car car1 = new Car();
        Class<? extends Car> car1Class = car1.getClass();
        Car car2 = car1Class.newInstance();
        Class<? extends Car> car2Class = car2.getClass();
        Car car3 = new Car();
        Class<? extends Car> car3Class = car3.getClass();
        System.out.println(car1Class);//class Car
        System.out.println(car1.hashCode());//1163157884
        System.out.println(car2Class);//class Car
        System.out.println(car2.hashCode());//1956725890
        System.out.println(car3Class);//class Car
        System.out.println(car3.hashCode());//356573597

        ClassLoader classLoader = car1Class.getClassLoader();
        System.out.println(classLoader);//AppClassLoader
        System.out.println(classLoader.getParent());//ExtClassLoader
        System.out.println(classLoader.getParent().getParent());//null,这里属于下面第二种可
        /**
         * 返回值为null一般有两种可能
         * 一、对象不存在
         * 二、java程序获取不到(比如底层代码是c/c++写的,java就获取不到)
         */
    }
}

双亲委派机制:为了保证安全 .class文件调用类加载器时,会进行以下三步(自我理解的)
1.类加载器接收到该类的请求
2.将这个请求向上委托给父类加载器,一直向上委托,直到启动类(根)加载器
3.启动类加载器检查是否能够加载这个类,能就加在加载结束,使用当前加载器, 否则,抛出异常,通知子加载器进行加载,重复步骤3 。如果都不能加载就会抛出ClassNotFound异常
用户自定义的Class Loader,继承ClassLoader—>AppClassLoader —>ExtClassLoader—>BootstrapClassLoader

四、Native

凡是带了native关键字的方法。说明java 的作用范得达不到了,会去调用起层C/C++的库
首先进入木地方法栈-------->调用本地方法本地接口(JNI)---------->调用本地方法库

JNI作用:为了扩展Java的使用,融合不同的编程语言为Java所用。
java刚诞生的时候C、C++横行,想要立足,必须要有调用C、C++的程序,所以它在内存区域中专门开辟了块标记区域本地方法栈:Native Method Stack,它的作用是登记native方法 ,它是线程私有
常见的java调用底层代码的例子
Java程序驱动打印机,管理系统

五、PC寄存器

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

六、 JVM栈

线程私有,生命周期与线程相同。

虚拟机栈描述的是Java方法执行的内存模型:每个方法被执行的时候都会同时创建一个栈帧(Stack
Frame)用于存储局部变量表、操作栈、动态链接、方法出口等信息。每一个方法被调用直至执行完成的过程,就对应着一个栈帧在虚拟机栈中从入栈到出栈的过程。
线程结束,栈内存也就释放了,所以对于栈来说,不存在垃圾回收问题

栈里存放的一般就是: 8大基本类型+对象引用+实例的方法
栈满了: StackOverflowError(错误,很严重)

七、方法区

方法区:Method Area
方法区是被所有线程共享,所有字段和方法的字节码,以及一些特殊方法如构造函数,接口代码也在此定义,简单说,所有定义的方法的信息都保存在该区域,此区域属于共享区间;
静态变量、常量、类信息(构造方法、接口定义)、运行时的常量池存放方法区中,但是实例变量存在堆内存中,和方法区无关(static final, Class, 常量池)

八、三种JVM

●Sun公司HotSpot Java HotSpot™ 64-Bit Server VM (build 25.181-b13, mixed mode)
●BEA JRockit
●IBM J9VM
一般是用的是HotSpot虚拟机,在cmd中可以用 java -version查看

九、堆(Heap)

一个JVM只有一个堆内存,堆内存的大小是可以调节的。 类加载器读取了类文件后,一般会把什么东西放到堆中?类,方法,常量,变量~,保存我们所有引用类型的真实对象;
在这里插入图片描述

GC垃圾回收,主要是在伊甸园区和养老区~
永久区用来存放JDK自身携带的Class对象,接口元数据,也就是存储的是ava运行时的一些环境或类信息,这个区域不存在垃圾回收!关闭虚拟机就会释放这个区域的内存。

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

对于永久区来说几个版本的变化
●jdk1.6及之前:永久代,常量池是在方法区;
●jdk1.7:永久代,但是慢慢的退化了,成为去永久代,常量池在堆中;
●jdk1.8及之后:无永久代,常量池在元空间

永久区/元空间实际不存在验证
在这里插入图片描述在这里插入图片描述
在测试的时候-Xmx最大内存设置小一点!
p

ublic class HeapTest {
    public static void main(String[] args) {
        long max = Runtime.getRuntime().maxMemory();//最大内存
        long total = Runtime.getRuntime().totalMemory();//初始化内存

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

        //-Xms1024m -Xmx1024m -XX:+PrintGCDetails 调完之后
        //1029177344字节	981.0MB
        //1029177344字节	981.0MB
        /**
         * Heap
         * PSYoungGen total 305664K
         * ParOldGen total 699392K
         * 新生区+老年区基本上占满了虚拟机的最大内存 981.0MB
         * 所以元空间逻辑上存在,物理上不存在
         * Metaspace
         */
    }
}

在实际业务中出现OOM
我们就可以先扩大内存试一下如果不行就分心内存,看具体是哪个地方出现了问题
我这里使用的是Jprofiler(内存分析快照工具)
再次增加参数**-Xms1m -Xmx8m -XX:+heapDumpOnOutOfMemoryError**

public class JProfilerTest {
    byte[] array = new byte[1024 * 1024];//1MB

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

在这里插入图片描述
找到对应文件使用Jprofiler打开就可以查看了。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

十、GC回收

GC两种类:轻GC (普通的GC),重GC (全局GC)
GC题目:
●JVM的内存模型和分区?
●堆里面的分区有哪些? Eden, form, to, 老年区,说说他们的特点!
●GC的算法有哪些?标记清除法,标记整理,复制算法怎么用?
●轻GC和重GC分别在什么时候发生?

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

复制算法
在这里插入图片描述
对象是从Eden区new出来的,如果满了就会触发轻GC活下来的进入幸存区,然后Eden区就空了,form区和to区它不是固定的,哪个区域是空的那个区域就是to区,当一个对象存活时间超过MaxTenuringThreshold设定值就会进入老年代。
●好处:没有内存的碎片~
●坏处:浪费了内存空间-:多了一半空间永远是空的to区. 假设对象100%存活(极端情况)
复制算法最佳使用场景:对象存活度较低的时候:新生代

标记清除法
在这里插入图片描述
●优点:不需要额外的空间!
●缺点:两次扫描,严重浪费时间,会产生内存碎片。

标记压缩
在这里插入图片描述
总结:
**内存效率:**复制算法>标记清除算法>标记压缩算法(时间复杂度)
**内存整齐度:**复制算法=标记压缩算法>标记清除算法
**内存利用率:**标记压缩算法=标记清除算法>复制算法

对于GC垃圾回收没有最好的算法,只有最合适的算法。所以 GC 也叫分代回收算法

年轻代:
●存活率低
●复制算法!
老年代:
●区域大:存活率
●标记清除+标记压缩混合实现

十一、JMM

1.什么是JMM?
JMM: Uava Memory Model的缩写),Java内存模型,不存在的东西,就是一种约定。
JMM定义了线程工作内存和主内存之间的抽象关系:线程之间的共享变量存储在主内存(Main Memory)中,每个线程都有一个私有的本地内存(Local Memory)
2.作用?
缓存一致性协议,用于定义数据读写的规则,保证了安全。

关于JMM的一些约定
线程解锁前,必须把共享变量立刻刷回主存;必须读取主内存中的最新值到工作内存中,加锁和解锁是同一把锁。

8种操作
在这里插入图片描述
在高并发的情况下我们需要解决可见性这个问题,因为每个线程操作完本地内存的值后会刷新(flush)回主内存,但是可能别的线程还没有读取到,就会产生一些问题。可以用volatile关键字来处理

public class JMMDemo {
    private volatile static int num = 0;
    public static void main(String[] args) { 
        new Thread(()->{ 
            while (num==0){
            }
        },"A").start();
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        num = 1;
        System.out.println(num);
    }

//结果等了1秒输出1,
//分析:线程A先执行(为了保证它先执行,所以让主线程睡一秒),程序一直在while循环里,直到一秒后,主线程执行,num=1;刷回主存,线程A立马读到了这个数据,结束while循环。

学到这里大体了解了JVM,但是需要继续深究!

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值