JVM基础概念入门

131 阅读5分钟

本文已参与「新人创作礼」活动,一起开启掘金创作之路。

JVM是什么

JVM又名Java 虚拟机,是Java程序的运行时环境。它是用C编写的一个软件,与其他软件一样它是运行在操作系统(LInux,win,mac等)的。那么操作系统又是运行在哪里的呢?

在计算机软件还未诞生之前,我们的计算机只能执行特定的程序,通过电路的拼接来运行程序。如果需要运行第二种程序就需要重新拼接一套新的电路然后运行,这就是早期的计算机硬件。后来冯诺依曼引出了计算机软件的浪潮,它想将程序 **‘保存’**起来,这就是计算机软件的开端。所以操作系统是运行在硬件之上的。JVM,操作系统,计算机硬件三者的关系是:

JVM的体系结构

JVM中最常说的就是调优,实际上在JVM的五块区域中,涉及调优的基本在于堆内存的调优,这是堆内存的机制所决定的。对于本地方法接口,就是native修饰的相关方法,这些方法是C语言编写的。在本地方法接口中存的是本地方法的信息,调用时就会去调用本地方法库中的本地方法。

类加载器

种类

  • 启动类加载器:Bootstrap ClassLoader\

  • 扩展类加载器:Extension ClassLoader\

  • 应用程序类加载器:Application ClassLoader\

不讨论自定义类加载器\

一个类的简单加载过程

测试代码,一看就理解

![1](1.gifpublic class ClassLoaderTest {
    public static void main(String[] args) {
        String str = new String("abc");
        System.out.println(str.getClass().getClassLoader());
//        System.out.println(classLoader.getParent());
        System.out.println("======我是分割线======");
        Person person = new Person();
        System.out.println(person.getClass().getClassLoader());
        System.out.println(person.getClass().getClassLoader().getParent());
        System.out.println(person.getClass().getClassLoader().getParent().getParent());
    }
}

各位看官看两个动图即可,我们知道String类是JDK中自带的一个类,是由启动类加载器加载的,启动类加载器在Java中是获取不到的。所以第二个动图就产生了空指针异常了。对比第一个动图中的Peron类是自定义的,所以加载最终是由应用型加载器加载完成的。为什么要打印三个类加载器,留个悬念。

三个类加载器的作用

1.启动类加载器:加载\lib目录中的,或者被-Xbootclasspath参数所指定的路径的类,并且是JVM识别的类库,用户无法获取。

2.扩展类加载器:加载\lib\ext目录中的,或者被java.ext.dirs系统变量所指定的路径中的类库。用户可以获取。

3.应用程序类加载器:该加载器也称系统加载器(System),是ClassLoader中getSystemClassLoader()方法的返回值。加载用户路径(ClassPath)所指定的类库。用户可以直接使用。用户可以获取

双亲委派机制

  1. 有类加载的需求

  2. 子类加载器会将类加载的请求传递给父类,层层递进到启动类加载器\

  3. 父类加载器如果加载不了就会反馈给子类自己去加载\

所谓的双亲,就是扩展类加载器与启动类加载器。双亲委派的好处在于不会让一个类重新加载两次,比如你自定义的java.lang.String是不合法的。

沙箱安全机制

沙箱可以理解为一个封闭的箱子,我们的程序只能在这个箱子里面运行。我们开发都是在本地开发,开发完成部署到远程服务器上,在Java中本地代码默认都是被信任的,也就是安全的。但是远程代码就不被信任,所以需要加一些安全机制保证程序的运行,比如远程代码操作操作系统与本地资源时要加授信的步骤,jdk6之后提出了域(domain)的概念,让沙箱更加安全。

Native

native代表是本地方法,拿Thread类举个例子,首先Thread不是抽象类也不是接口,凭什么有方法可以不用写方法实现?原因就是这个方法被native修饰了。在Java中,凡是被native修饰的就是本地方法。Java只存有它的标记,用到时回去调用本地方法库。

PC寄存器

PC寄存区也叫程序计数器,我们知道多线程情况下,CPU是高速轮转的,会给每个线程分配时间片,这就存在程序执行一半去执行其他的线程,那么下次回到该线程下,CPU如何知道程序执行到哪里了? 答案就是程序计数器,这个可以理解为指针,它会指向下一个指令的内存地址。PC寄存器是线程私有的,每个线程都会有个PC寄存器,这是保证了线程间的相互独立。

栈也称Java虚拟机栈,它的特点是先进后出,这与它的数据结构有关系。栈里面是一个个的栈帧,每个栈帧实际对应一个要执行的方法。每一个方法执行完就会弹栈消失,一个程序执行完的标志就是栈内没有栈帧。通常栈中存储的都是一些引用,所以程序执行完,栈中基本不会剩余什么垃圾,所以基本不会出现OOM。可能异常的情况是入栈的栈帧超过栈最多可容纳的栈帧,会报栈溢出异常,另一种情况就是当前栈帧所申请内存超过最大内存,就会出现OOM的问题。

Java中new的对象都存放在堆中,所谓的垃圾回收是回收对象。所以我们的所说的调优其实大多都在调堆。下面是堆内存的结构图。