注意:垃圾回收算法周阳老师讲的有错误,具体在p19,四大垃圾回收算法为复制算法、标记-整理算法、标记-清除算法、分代收集算法(不是引用计数算法)。这里感谢@9c0bd0ceebfa指出。下文已经更正正确,请放心食用。

JVM调优调的是什么?
1,尽量减少GC的操作;
2,参数调优

JVM 位置

image.png

JVM 体系结构概览

image.png
注意:
我们平时说的栈是指的 Java 栈,native method stack 里面装的都是 native 方法。见下文↓

类装载器

image.png

注意:

  • 方法区并不是存放方法的区域,其是存放类的描述信息(模板)的地方。
  • Class loader只是负责 class 文件的加载,相当于快递员,这个“快递员”并不是只有一家,Class loader 有多种(启动类加载器、拓展类加载器、应用程序加载器、自定义类加载器)
  • 加载之前是“小class”,加载之后就变成了“大Class”,这是安装java.lang.Class模板生成了一个实例。“大Class”就装载在方法区,模板实例化之后就得到n个相同的对象
  • JVM并不是通过检查文件后缀是不是.class来判断是否需要加载的,而是通过文件开头的特定文件标志(cafe babe)

image.png

image.png

注意:

  • JDK1.8之前,硬件决定执行性能,1.8之后 JVN可以不完全受到硬件的性能制约
  • Class loader有多种,可以说三个,也可以说是四个(第四个为自己定义的加载器,继承 ClassLoader),系统自带的三个分别为:
    1. 启动类加载器(Bootstrap) ,C++所写,涉及JVM的本地实现,获取不到(如:new Object().getClass().getClassLoader() 得到的是 null),加载JDK里的API,如java.* ,java.lang
    2. 扩展类加载器(Extension) ,Java所写,对应的是 jre\lib\ext*,平台类加载器
    3. 应用程序类加载器(AppClassLoader)。

我们自己 new 的时候创建的是应用程序类加载器(AppClassLoader)。

  1. import com.gmail.fxding2019.T;
  2. public class Test{
  3. //Test:查看类加载器
  4. public static void main(String[] args) {
  5. Object object = new Object();
  6. //查看是那个“ClassLoader”(快递员把Object加载进来的)
  7. System.out.println(object.getClass().getClassLoader());
  8. //查看Object的加载器的上一层
  9. // error Exception in thread "main" java.lang.NullPointerException(已经是祖先了)
  10. //System.out.println(object.getClass().getClassLoader().getParent());
  11. System.out.println();
  12. Test t = new Test();
  13. System.out.println(t.getClass().getClassLoader().getParent().getParent());
  14. System.out.println(t.getClass().getClassLoader().getParent());
  15. System.out.println(t.getClass().getClassLoader());
  16. }
  17. }
  18. /*
  19. *output:
  20. * null
  21. *
  22. * null
  23. * sun.misc.Launcher$ExtClassLoader@4554617c
  24. * sun.misc.Launcher$AppClassLoader@18b4aac2
  25. * */

注意:

  • 如果是JDK自带的类(Object、String、ArrayList等),其使用的加载器是Bootstrap加载器;如果自己写的类,使用的是AppClassLoader加载器;Extension加载器是负责将把java更新的程序包的类加载进行
  • 输出中,sun.misc.Leauncher是JVM相关调用的入口程序,具体位置:解压 rt.jar 之后可以看到 java\jre\lib\rt.jar\sun\com\misc\Launcher.class 文件。
  • Java加载器个数为3+1。前三个是系统自带的,用户可以定制类的加载方式,通过继承Java. lang. ClassLoader。

image.png

双亲委派

编译: 从上往下执行,先从系统类库(JDK)中查找这个类,再往下一致查找classpath,若没有,则会抛出编译异常

执行: 自低向上查找,先从classpath中查找,若没有,则继续从父加载器中查找,直到Bootstrap,若找不到,则会抛出ClassNotFoundException异常 ———————————————— 版权声明:本文为CSDN博主「桂花很香,旭很美」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。 原文链接:https://blog.csdn.net/weixin_40959890/article/details/107183798

image.png

为什么 Java 要设计双亲委派机制?(双亲委派的好处)

  • 防止类的重复加载(如果calsspath中找到就不会在往上查找)
  • 安全(防篡改字节信息)

注意:

  • 双亲委派机制:“我爸是李刚,有事找我爹”。
    例如:需要用一个A.java这个类,首先去顶部 Bootstrap 根加载器去找,找得到你就用,找不到再下降一层,去 Extension 加载器去找,找得到就用,找不到再将一层,去 AppClassLoader 加载器去找,找得到就用,找不到就会报”CLASS NOT FOUND EXCEPTION“。
  1. //测试加载器的加载顺序
  2. package java.lang;
  3. public class String {
  4. public static void main(String[] args) {
  5. System.out.println("hello world!");
  6. }
  7. }
  8. /*
  9. * output:
  10. * 错误: 在类 java.lang.String 中找不到 main 方法
  11. * */

上面代码是为了测试加载器的顺序:首先加载的是 Bootstrap 加载器,由于JVM中有 java.lang.String 这个类,所以会首先加载这个类,而不是自己写的类,而这个类中并无 main 方法,所以会报“在类 java.lang.String 中找不到 main 方法”。

这个问题就涉及到,如果有两个相同的类,那么java到底会用哪一个?如果使用用户自己定义的 java.lang.String,那么别使用这个类的程序会去全部出错,所以,为了保证用户写的源代码不污染 java 出厂自带的源代码,而提供了一种“双亲委派”机制,保证“沙箱安全”。即先找到先使用。

执行引擎

执行引擎负责执行运行时数据区加载的函数,JNI接口包括本地函数库的调用。Java没有C、C++运行快的原因:Java面向虚拟机的指令编程,而不是面向软件的应用编程。
image.png

Native Interface 本地接口

image.png

Thread类的start方法如下:

  1. public synchronized void start() {
  2. /**
  3. * This method is not invoked for the main method thread or "system"
  4. * group threads created/set up by the VM. Any new functionality added
  5. * to this method in the future may have to also be added to the VM.
  6. *
  7. * A zero status value corresponds to state "NEW".
  8. */
  9. if (threadStatus != 0)
  10. throw new IllegalThreadStateException();
  11. /* Notify the group that this thread is about to be started
  12. * so that it can be added to the group's list of threads
  13. * and the group's unstarted count can be decremented. */
  14. group.add(this);
  15. boolean started = false;
  16. try {
  17. start0();
  18. started = true;
  19. } finally {
  20. try {
  21. if (!started) {
  22. group.threadStartFailed(this);
  23. }
  24. } catch (Throwable ignore) {
  25. /* do nothing. If start0 threw a Throwable then
  26. it will be passed up the call stack */
  27. }
  28. }
  29. }
  30. private native void start0();

Thread类中竟然有一个只有声明没有实现的方法,并使用native关键字。用native表示,也此方法是系统级(底层操作系统或第三方C语言)的,而不是语言级的,java并不能对其进行操作。native方法装载在native method stack中。

PC 寄存器

image.png

  • 注意:native方法不归 java 管,所以计数器是空的

image.png

上面图中是亮色的地方有两个特点:

  1. 所有线程共享(灰色是线程私有)
  2. 存在垃圾回收(有数据共享,才会存在垃圾回收)

方法区(Method Area)

image.png

注意:

  • 方法区:绝对不是放方法的地方,它是存储的每一个类的结构信息(比如static)
  • 永久代和元空间的解释:方法区是一种规范,类似于接口定义的规范:List list = new ArrayList();把这种比喻用到方法区则有:
    1. java 7中:方法区 f = new 永久代();
    2. java 8中:方法去 f = new 元空间();

栈(Stack)

image.png

栈帧的概念 java中的方法被扔进虚拟机的栈空间之后就成为“栈帧”,比如main方法,是程序的入口,被压栈之后就成为栈帧。

注意:

  • 栈管运行(入栈、出栈),堆管存储(new 出来的对象等)。
  • 栈是线程私有的,不存在垃圾回收。
  • 栈的生命周期:随着线程的创建而创建,随着线程的销毁而释放。
  • 栈中主要保存什么?
    • 本地变量
    • 栈操作
    • 栈帧数据

image.png

image.png

image.png

  1. public class Test{
  2. public static void m(){
  3. m();
  4. }
  5. public static void main(String[] args) {
  6. System.out.println("111");
  7. //Exception in thread "main" java.lang.StackOverflowError
  8. m();
  9. System.out.println("222");
  10. }
  11. }
  12. /*
  13. *output:
  14. * 111
  15. * Exception in thread "main" java.lang.StackOverflowError
  16. * */

注意:

  • StackOverflowError是一个“错误**,而不是“异常”**。


image.png
image.png

注意:

  • Java 堆中存放的是访问类元数据的地址。
  • HotSpot:如果没有明确指明,JDK的名字就叫HotSpot

  • image.png

  • 元数据:描述数据的数据(即模板,也就是“大Class”)
    上面的关系图的一个实例为下图:

image.png