jvm之初识

一、jvm体系结构:

这里写图片描述

二、.class文件:

这个众所周知,这里就不废话了,.class文件就是javac编译之后产生的文件

三、类装载器- - - classloader:

1、作用:ClassLoader只负责class文件的加载,至于它是否可以运行,怎么运行,则由Execution Engine决定

2、分类:虚拟机自带的加载器、用户自定义的加载器

3、虚拟机自带的加载器:

分类:

  • 启动类(bootstrap)加载器 由c++写的
  • 扩展类(extension)加载器 由java写的
  • 应用程序(app)加载器,也叫系统类加载器,加载当前应用的classpath的所有类 由java写的

代码实现:

1、写一个类(比如student),然后打成jar包,将这个jar包放入jdk的jre的lib的ext包中即可

这里写图片描述

2、然后进行代码实验:

 Object o = new Object();
 System.out.println(o.getClass().getClassLoader());
 System.out.println("======================================");
 Class clazz = Class.forName("Myjvm.MyStudent");
 System.out.println(clazz.getClassLoader());
 System.out.println("======================================");
 ClassloaderTest classloaderTest = new ClassloaderTest();
 System.out.println(classloaderTest.getClass().getClassLoader());

运行结果如下:

这里写图片描述

解释说明:

  • 为什么第一个object的类加载器是null呢,因为object是java自带的类,java自带的类在加载的时候调用的是bootstrap类加载器,而只要你用的是bootstrap这个根加载器去加载的类,返回的加载器类型都是null
  • 第二个是ExtClassLoader,只要是自己写的,不是java自带的,然后打成一个jar包的形式放到jdk/jre/lib/ext的目录下的类,调用的都是java扩展类加载器
  • 第三个是AppClassLoader,只要是在classpath下的所有类都是调用的是应用程序加载器

三者关系: 其中sum.misc.lancher是一个java虚拟机的入口应用

这里写图片描述

结果:

  • 启动类(bootstrap)加载器是扩展类(extension)加载器的父类
  • 扩展类(extension)加载器是 应用程序(app)加载器的父类

类加载器的双亲委派机制:

即:一个类在被加载的时候它会先去委托父类加载器,如果父类加载器可以完成加载,那么就成功加载,如果父类加载器无法完成加载,才轮到自己去加载。

目的:主要是为了保证java的一种安全机制,叫做沙箱机制

沙箱机制:

我相信大家一定在初学java的时候自己定义过类似于String这样的类,虽然编译的时候不报错,但是运行时就是报错(在idea中索性都没有了运行的那按钮,eclipse还能运行)。。。

这里写图片描述

当时的你一定是一脸懵逼,这是因为双亲委派机制使得java在加载String这个类的时候默认加载的是java自带的String类,由bootstrap加载器进行加载,不会去加载我写的String类,这样一来就保证了java源代码的安全。我们再找到java自带的String这个类,打开rt这个jar包(注意,java自带的类基本都在rt这个jar中

这里写图片描述

我们可以看到反编译出来的java自带的String类中并没有main方法

这里写图片描述

四、执行引擎:

在java中javac负责编译,java负责执行,执行引擎负(Execution Engine)责解释命令,提交操作系统执行。

五、JNI:

Java Native Interface:作用是融合不同的编程语言为Java所用,它的初衷是融合 C/C++程序

代码中的例子:我们可以打开我们最熟悉的一个类(Object)的源码

这里写图片描述

像这种都是本地方法,被native修饰的方法,只有方法的声明,没有方法体,说明java代码到此为止,后面就开始调底层操作系统,或者是其他函数库,它的具体做法是Native Method Stack中登记native方法,在Execution Engine执行时加载本地方法库。

注意:我们平时说的栈是java栈,我们的main等java方法放入的是java栈中,但是native方法放入的是native method stack中。

六、pc寄存器:

思考:有没有人想过,为什么程序能按逻辑执行下来,不出错?那是因为有寄存器的存在。

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

七、栈(Stack):

概要:栈也叫栈内存,主管Java程序的运行,是在线程创建时创建,它的生命期是跟随线程的生命期,线程结束栈内存也就释放,对于栈来说不存在垃圾回收问题,只要线程一结束该栈就结束,生命周期和线程一致,是线程私有的。

经典错误:

Exception in thread “main” java.lang.StackOverflowError

这里写图片描述

分析说明:main方法进来会先放入栈里面,然后调用hello这个方法,hello这个方法又去调本身,然后就产生了死循环,递归调用。然后栈就被塞满了。

存储内容:基本类型,引用变量和我们写的实例方法(如main方法等,值得注意的是:栈的最底层是main方法)

八、方法区:

注意:在java7之前,方法区就是永久代

内容:方法区是线程共享的,通常用来保存装载的类的元结构信息。比如:运行时常量池+静态变量+常量+字段+方法字节码+在类/实例/接口初始化用到的特殊方法等。我们在new一个对象的时候需要有相同的属性,这些东西就去方法区拿,方法区就是存储这些东西的。

九、堆(Heap):

说明:一个JVM实例只存在一个堆内存,堆内存的大小是可以调节的。类加载器读取了类文件后,需要把类、方法、常变量放到堆内存中,保存所有引用类型的真实信息。

堆结构:

  • 逻辑上分为三块:新生区+养老区+永久区(java8之后是元空间)
  • 实际上堆内存只有两块,新生区和养老区,永久区又称为非堆内存
    这里写图片描述

说明: 每次刚new出来的对象都会在新生区的伊甸区,但是伊甸区空间是有限的,当对象越来越多,就慢慢的进行轻量的gc,这个时候将会有一部分对象被清除,
幸存下来的将会去幸存0区,如果说又不停的new对象,幸存0区也满了,那就去幸存者1区,以此类推,如果幸存者1区也满了就去养老区,据说一般需要幸存15次左右才能到养老区,如果养老区也满了就回触发重量级的gc,清除对象来释放内存。

注意: 幸存者0区和1区也称为from区和to区,0区和1区是会来回交换的,即0区轻量gc后将幸存者移入1区,然后0区重新获取一片空的空间,变成幸存者1区,原来的1区将会变成0区

经典错误:内存溢出- - - OOM

Exception in thread “main” java.lang.OutOfMemoryError: Java heap space

public static void main(String[] s) {
        String str = "mzd";
        while (true) {
            str += str;
        }
    }

设置jvm内存参数,并打印gc日志(现在配置内存为2M)

这里写图片描述
这里写图片描述

运行结果如图所示:

这里写图片描述

注意:

  • -xms:表示jvm初始内存 默认是计算机物理内存的1/64
  • -xmx:表示jvm最大内存 默认是计算机物理内存的1/4
  • -XX:+PrintGCDetails:表示打印详细gc日志
public static void main(String[] s) {
        // 最大内存
        long maxmemory = Runtime.getRuntime().maxMemory();
        // 当前总内存---返回的是java虚拟机现在已经从操作系统那里挖过来的内存大小,
        // 也就是java虚拟机这个进程当时所占用的所有 内存
        long totalmemoty = Runtime.getRuntime().totalMemory();
        //java虚拟机内存100%的情况下是会稍微多获取操作系统一点的内存,但是又没有用上的内存,实际上就是 freeMemory()
        long freememory = Runtime.getRuntime().freeMemory();
        System.out.println(maxmemory / (double) 1024 / 1024 + "M");
        System.out.println(totalmemoty / (double) 1024 / 1024 + "M");
        System.out.println(freememory / (double) 1024 / 1024 + "M");
    }

十、OOM调优之MAT:

研究中。。。敬请期待!!!

猜你喜欢

转载自blog.csdn.net/tuesdayma/article/details/79600075