jvm是一个虚拟机,用于运行java代码,类的编译到运行主要为一下:
通过javac.exe编译,产生class文件,然后通过类加载器加入jvm:
类加载器:
引导加载器:使用c++编写,负责java的核心库,只能底层调用,无法直接获取。
扩展加载器:在jdk\bin\ext文件下,拓展的jar包,
系统加载器:最常用的加载器,它负责加载用户路径(ClassPath)上所指定的类库。我们自己编写的代码以及使用的第三方的jar包都是由它来加载的。
自定义加载器:就是我们自己编写的类。
类加载器有一个很重要的机制:
双亲委派机制
是为了程序的安全产生的,他的效果是:
会一直委派到最底层的加载器:
引导加载器<——扩展加载器<——系统加载器<——自定义加载器<——代码
然后一级一级寻找类,为什么这样可以保证安全?
因为:很多类都是已经写好的了(String),非常多的工具、框架中都使用了这个类,如果有一天,我自己也写了这样一个类(String),名字和它一模一样,那么,程序到底用谁的类呢?
所以双亲委派机制解决了这个问题,程序直接用原来的类,因为从逻辑上知道,这个类是对的。
虚拟机栈:
里面主要存放:对象引用地址+8大基本类型+局部变量+实例方法
存放程序的执行步骤,为什么要怎么说呢?
我们都知道,main方法是最先执行的,那么,它到底什么时候结束的呢?我想这个问题大家都知道,main方法是最后结束的,所以这就说明了,程序调用时用栈实现的。
本地方法栈:
存放调用的本地接口方法,如果大家查看过多线程的类,会发现,底层开启线程并不是java实现的,调用了一个方法:
native就是用来标记该方法为调用本地方法,也就是本地的c++方法,所以jvm的底层其实就是c++。
然后我们就来说一下堆:
这玩意就是jvm核心的地方,也是最麻烦的地方
堆中包含如下:
堆中主要存放实力对象!
新生代,老年代,伊甸园、幸存区是啥呢?
新生代指的是在程序运行时,创建的类,会被GC垃圾清理机制清理掉,而老年代指的是有些类经常使用,不会被清理。
一旦这个类多次被使用了,就会放去幸存区,如果再多次调用,就会放去老年区。
也就是说,伊甸园>幸存区,GC优先清理伊甸园,GC清理机制分为普通清理和加强清理,当普通GC清理不了的时候,就调用加强GC清理。所以中99%的垃圾处理都在堆中处理,栈中是不会存在的,因为栈一旦使用,就出栈了。
GC清理机制用白话说挺简单,就是判断改类是否被用,一个类被调用时,栈就会指向它,是一个双向,类似双向链表,所以一旦为null,就可以回收啦~
方法区:主要放static修饰的变量,方法,final常量,类的信息
这里还有一个知识点,堆、方法区是线程共享的,谁都可以调用,也就是为什么,static全部地方都可以使用。
程序计数器:
看到这个,我就想起了cpu也有这玩意,用来记录执行的指令,它们都是一样的效果,这玩意也是拿来保存指令,记录下一条指令是啥。
然后就是写过递归的同志都遇见过的报错:StackOverFlowError栈溢出
这玩意就是调用过多的方法,导致栈爆满,一般情况都是死递归调用,闭环。
还有一个就是学习的时候根本遇不到,只能刻意的创建(因为学习肯定不会创建这么多的对象)
StackOverFlowError内存溢出,因为对象创建太多了,堆都装满了,死循环new对象。。。。
解决办法就是先提高堆的内存,看看是否再次报错,用idea就是如下:
然后就是导出dump文件,使用jprofiler软件查看,具体看视频:
9、使用JPofiler工具分析OOM原因_哔哩哔哩_bilibili