关于JVM的组成与classloader

JVM有什么好处:

一次编写,到处报错

自动内存管理,垃圾回收机制

 JVM执行流程:由java源码编译成字节码(class)文件,再由类加载器对class文件进行装载,将其加入到运行时数据区;运行时数据区可以调用native方法、jit优化器、解释器等

 

java - v x.class //打印堆栈大小,局部变量的数量和方法的参数

javap -v x.class //打印类的元空间信息

 

JVM组成

1.什么是程序计数器

每个线程都私有一份的,内部保存字节码的行号。用于记录正在执行的字节码指令的地址。

例如A线程在执行到第10行时,时间片被B线程抢走此时A线程会失去执行权,这时程序计数器会记录下当前行号,当执行权再次被分给A时,A会在上一次执行的基础上继续往下执行

 

2.java中的堆是什么

堆是线程共享的区域,共享对象实例、数组等,当堆没有更多的内存空间分配给实例,无法再拓展时,就会出现OOM问题

堆内有年轻代、老年代两部分:年轻代被化为eden、S0、S1;根据JVM策略,在eden中未被垃圾回收的实例会进入S0或S1;在经历数次回收后仍存在的实例会进入老年代;老年代是保存一些生命周期长的对象


3.虚拟栈又是什么

栈的特点是先进后出。在线程运行时所需要的内存,即虚拟机栈。

虚拟机栈由多个栈帧组成,对应着方法调用所占用的内存;栈帧一般包含了参数、局部变量、返回地址

每个线程只能有一个活动栈帧,即正在执行的方法

 

4.栈内存分配是越大越好吗?

默认帧栈内存是1MB,栈帧过大会导致线程数变少。

 

5.方法内的局部变量是线程安全的吗?

如果是在方法内创建并消费,那么局部变量就是安全的。但是如果局部变量由引用获得并逃离了方法的作用范围,那就是不安全的。

 

6.栈内存溢出情况

栈帧过多,栈帧过大(不常见)

 

7.堆栈的区别是什么

栈内存主要用于存储局部变量和方法调用;堆内存主要用于存储java对象和数组。堆会垃圾回收,栈不需要

栈内存是线程私有的;堆内存是线程共有的

栈内存报错是stackoverflow;堆内存保存是OOM

 

8.什么是方法区method area/元空间metaspace

元空间原属于堆的永久代,后单独出来。主要用于存储类的信息、运行时常量池。

在java1.7及之前还有永久代,现在被改为了本地内存中的metaspace,metaspace主要用于存储类信息,静态变量,常量,编译后的代码。

在动态类加载会越来越多的情况下,永久代会出现内存不可控的状态,一旦内存设小极有可能出现OOM问题。因此在java8之后把永久代转移到了本地内存,一定程度上防止了OOM。

但是如果metaspace的内存无法满足分配的请求时,也会报错oom:Metaspace(默认情况下metaspace的大小是没有上限的,我们可以通过vm options设置-XX:MaxMetaspaceSize来指定大小)

 

9.什么是常量池/运行时常量池

常量池可以看作为一张表,虚拟机指令根据这张表找到要执行的类名、方法名、参数类型、字面量等

当类被加载时,常量池信息就会被放入运行时常量池,并把里面的符号地址转换为真实地址

 

10.你听过直接内存吗

常规IO会在用户态(java)调用内核态,内核套需要调用java堆缓冲区,而java堆缓冲区又需要调用系统内存的缓冲区,由系统内存缓冲区来读取文件最终返给java

在NIO中,java堆内存和系统内存是公用的,减少了一层不必要的调用,这就是直接内存。

直接内存不由JVM管理,是由系统内存管理的。使用直接内存少了一次缓存的使用,能够提高性能。直接内存分配读写开销大,且不受JVM内存回收管理

 

类加载器classloader

1.什么是类加载器,有哪些

JVM只能运行二进制文件,类加载器的作用是将字节码加载到JVM中,让java文件运行起来

一般有四个:

启动类加载器(bootstrap classloader)加载java_home/jre/lib目录的库

扩展类加载器(ext classloader)加载java_home/jre/lib/ext目录的类

应用类加载器(app classloader)加载classpath下的类(一般来说就是我们自己写的类)

自定义类加载器,tomcat就是基于j2ee实现的

 

2.什么是双亲委派模型

加载某一个类,先委托上一级的加载器进行加载,如果上级加载器也有上级,则会继续向上委托,如果该类委托上级没有被加载,子加载器尝试加载该类

使用双亲委派机制,保证类不会重复加载,确保唯一性。同时保证类库API不会被修改

 

3.类装载的执行过程

加载:查找和导入class文件——根据类的二进制数据流,在metaspace中保存类的数据结构,在堆中开辟空间保存其对应的对象

验证:保证类加载的准确性——对文件进行检查,查看类的文件格式,语法,字节码等相关信息是否错误。同时class文件会在常量池通过字符串记录自己要使用的方法,检查他们是否存在

准备:为类变量分配内存并设置类变量的初始值——(static变量、static修饰的final的引用类型分配空间在此阶段完成,赋值在初始化阶段完成;static修饰的final的基本类型与字符串常量赋值都在此阶段完成)

解析:把类中的符号引用转换为直接引用

初始化:对类的静态变量,静态代码块执行初始化操作——(初始化一个类时,如果父类尚未初始化则优先初始化其父类;同时包含多个静态方法、变量,则应按照从上到下的顺序执行)

使用:JVM开始从入口方法开始执行用户的程序代码

卸载:用户执行完程序后,jvm销毁创建的class对象

posted @ 2024-11-04 21:00  天启A  阅读(18)  评论(0)    收藏  举报