解锁JVM成神之路(一)

:2019年10月10日 小白学java
分享到:

jvm指的是Java虚拟机,一种能够运行Java字节码的虚拟机。它能够模拟具有完整硬件系统功能,运行在一个完全隔离环境中的完整计算机系统。作为一种编程语言的虚拟机,它不只是专注于Java语言,只要生成编译...

jvm指的是Java虚拟机,一种能够运行Java字节码的虚拟机。它能够模拟具有完整硬件系统功能,运行在一个完全隔离环境中的完整计算机系统。作为一种编程语言的虚拟机,它不只是专注于Java语言,只要生成编译文件匹配jvm对加载编译文件格式的要求,任何语言都可以由jvm编译运行。比如scala,kotlin等。

一,JVM的基本组成结构。

jvm由三部分组成:

1)类加载子系统

2)运行时数据区(内存空间,内存结构)

3)执行引擎

结构图如下:

点击添加图片描述(最多60个字)

深入了解运行时数据区:

线程共享:

①方法区(1.8之后已经不叫方法区了,叫元数据区):用来存储被jvm加载的类信息,常量,静态变量等数据。也就是类的所有属性,方法以及构造器,接口代码都在这里定义。

这个区域包含一个很重要的区域叫常量池。class对象除了类的版本,属性,方法等信息外,还有一些常量以及字面值,而这些几乎都存在于常量池里,这部分内容将在被加载的过程中存放到常量池里。

②堆:实例化的对象以及数组都存在这个区域。这个区域的cg最活跃,也就是cg进行垃圾回收的重要场所。这个区域,cg是以分代回收算法进行垃圾回收的。从cg的角度看,堆又可以分为:新生代和老年代。

线程私有:

③程序计数器:也就是一个指针,指向方法区中的方法字节码。也就是用于存储每条线程的执行字节码指令地址。每条线程都会拥有自己的程序计数器,以便线程切换的时候,能使程序正常运行。

④jvm栈:存储着每一个栈帧。每个方法的执行都对应着每一个栈帧,栈帧里放着局部变量表,操作数栈,动态连接,方法出口等信息。每个方法的执行以及结束都对应到栈帧的入栈与出栈。

⑤本地方法栈:这个跟jvm栈差不多,只是jvm栈为我们的java服务的,而本地方法栈是为native 方法服务。

深入了解类加载子系统:

我们的Java文件被jvm编译器编译成class对象之后,在runtime时,由类加载子系统将class字节码文件加载到运行时数据区中。

但是,类加载子系统在类的整个生命周期中主要的工作是什么呢?

类的生命周期:

类的生命周期分为:

 加载-->连接-->初始化-->使用-->卸载

而连接过程又分为:验证-->准备-->解析。其实这个过程就是类加载子系统工作的过程。

一,工作过程

1,加载:将class字节码文件加载到运行时数据区中。

2.1,验证:1,检查字节码文件是否正确。2,检查文件头的magic number是否正确。

何为magic number?我们用编辑器打开随意一个class文件,发现都是以CAFEBABE开头。把这数据改掉或者删除,都会出错。这个就是magic number

2.2,准备:给类的静态变量分配内存,赋予初始值。这里的初始值并不是赋值的值。打个比方:private int i = 100; 在准备阶段,并不是将100赋值给i。而是给i赋值0,因为int的默认初始值是0。

2.3)解析:检查指定的类是否引用了其他的类或者接口,是否能正常引入。也就是装载当前类引入依赖的其他类。

3)初始化:这里才是给类的静态变量赋正确的初始值。i的值由0变为100。

使用,卸载这就没什么可说的了。

通过源码了解加载过程:

在rt.jar包中的java.lang.ClassLoader类中,我们可以查看类加载实现过程的代码,具体源码如下:

protected Class<?> loadClass(String name, boolean resolve)

throws ClassNotFoundException

{

synchronized (getClassLoadingLock(name)) {

// First, check if the class has already been loaded

Class<?> c = findLoadedClass(name);

if (c == null) {

long t0 = System.nanoTime();

try {

if (parent != null) {

c = parent.loadClass(name, false);

} else {

c = findBootstrapClassOrNull(name);

}

} catch (ClassNotFoundException e) {

// ClassNotFoundException thrown if class not found

// from the non-null parent class loader

}

if (c == null) {

// If still not found, then invoke findClass in order

// to find the class.

long t1 = System.nanoTime();

c = findClass(name);

// this is the defining class loader; record the stats

sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);

sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);

sun.misc.PerfCounter.getFindClasses().increment();

}

}

if (resolve) {

resolveClass(c);

}

return c;

}

}

源码的注释写得很清楚了:

1)加载的时候使用了synchronized加锁机制,也就是当前线程有在加载当前这个类时,不允许其他类进行加载。

2)判断这个类是否已经被加载,如果已经加载了,就不再加载。如果没有,则要加载。

我们在看源码时,看到

 //从parent这个成员变量中,我们得知

 private final ClassLoader parent;

try {

if (parent != null) {

c = parent.loadClass(name, false);

   } else {                        

      c = findBootstrapClassOrNull(name);                    

   }                

  }

这是一个很重要的信息~由此引申一个很重要的概念

二,双亲委派机制

我们先了解类加载器的种类:

1,启动类加载器Bootstrap ClassLoader:负责将java_home/lib下的类加载到内存中

2,扩展类加载器Extension ClassLoader:负责将java_home/lib/extde的类加载到内存中

3,系统类加载器Application ClassLoader:负责将classpath的类加载到内存中

4,用户自定义加载器User ClassLoader:负责加载用户自定义路径下的类包

在看源码可以看出,每次加载类的时候,当前加载器都将该类一层一层交给parent,而且每次都检查是否已经加载了,如果加载了就不再加载。当交给parent时,如果parent能加载,则就加载。如果parent都不能加载,则由当前加载器进行加载。

双亲委派的优势:

沙箱安全机制:比如自己写的String.class类不会被加载,这样可以防止核心库被随意篡改

避免类的重复加载:当父ClassLoader已经加载了该类的时候,就不需要子CJlassLoader再加载一次

以上就是jvm的内存结构以及类加载子系统的相关内容。jvm还有许许多多的知识,比如垃圾回收机制,调优等,以后我们慢慢了解

[我要纠错]
文:王振袢&发表于江苏
关键词: 指的 Java 虚拟 一种 能够

来源:本文内容搜集或转自各大网络平台,并已注明来源、出处,如果转载侵犯您的版权或非授权发布,请联系小编,我们会及时审核处理。
声明:江苏教育黄页对文中观点保持中立,对所包含内容的准确性、可靠性或者完整性不提供任何明示或暗示的保证,不对文章观点负责,仅作分享之用,文章版权及插图属于原作者。

点个赞
0
踩一脚
0

您在阅读:解锁JVM成神之路(一)

Copyright©2013-2024 JSedu114 All Rights Reserved. 江苏教育信息综合发布查询平台保留所有权利

苏公网安备32010402000125 苏ICP备14051488号-3技术支持:南京博盛蓝睿网络科技有限公司

南京思必达教育科技有限公司版权所有   百度统计

最热文章
最新文章
  • 卡尔蔡司镜片优惠店,镜片价格低
  • 苹果原装手机壳