JVM入门(1)

mac2025-07-17  9

文章目录

1.JVM是什么2.JVM跟我们平时运行在机器上的系统之间是什么关系3.类加载器的概念4.字节码执行引擎的概念5.JVM在什么情况下会加载一个类?6.类加载的过程6.1**验证阶段**6.2**准备阶段**6.3**解析阶段**6.4核心阶段:初始化 7.类加载器和双亲委派机制7.1类加载器7.2双亲委派机制 8.如何对“.class”文件处理保证不被人拿到以后反编译获取公司源代码?9.**什么是JVM的内存区域划分?**1.**方法区**2.**执行代码指令用的程序计数器**3.**Java虚拟机栈**4.**Java堆内存**5.本 地方法栈 10.Tomcat这种Web容器中的类加载器应该如何设计实现?11.对象创建过程,对象在Java堆内存里会占用多少内存空间呢?1.对象创建过程2.对象在Java堆内存里会占用多少内存空间3.对象访问定位 12.**不再需要的那些对象应该怎么处理?**

1.JVM是什么

答:负责运行java编译好的字节码文件(.class),也就相当于是负责运行我们写好的系统。

2.JVM跟我们平时运行在机器上的系统之间是什么关系

答:JVM具有跨平台性,可以在多种系统上执行。

3.类加载器的概念

答:JVM要运行这些“.class”字节码文件中的代码,首先得把这些“.class”文件中包含的各种类给加载进来,把编译好的那些".class"字节码文件给加载到JVM中。

4.字节码执行引擎的概念

答:执行加载到内存里的我们写好的那些类了

5.JVM在什么情况下会加载一个类?

Putstatic,getstatic,invokeStatic反射初始化子类的时候,父类没有初始化会从初始化父类启动程序那个类反射的时候newInstant

过程:加载 -> 验证 -> 准备 -> 解析 -> 初始化 -> 使用 -> 卸载

6.类加载的过程

6.1验证阶段

根据Java虚拟机规范,来校验你加载进来的“.class”文件中的内容,是否符合指定的规范

6.2准备阶段

这个阶段是给加载进来的类分配好了内存空间,类变量(也就是static修饰的变量)也分配好了内存空间,并且给了默认的初始值,这个概念,大家心里一定要有。

6.3解析阶段

实际上是把符号引用替换为直接引用的过程

6.4核心阶段:初始化

初始化过程是执行类构造器<client>()方法的过程

类构造器client<>() 会对的所有类变量(静态变量)的赋值动作和静态语句块(static{})中的语句合并并按照出现的顺序决定的

7.类加载器和双亲委派机制

7.1类加载器
启动类加载器 (Bootstrap ClassLoader) JVM启动,首先就会依托启动类加载器,去加载Java安装目录下的“lib”目录中的核心类库。扩展类加载器(Extension ClassLoader) 加载这个“lib\ext”应用程序类加载器(Application ClassLoader) 负责去加载“ClassPath”环境变量所指定的路径中的类自定义类加载器
7.2双亲委派机制

**双亲委派模型:**先找父亲去加载,不行的话再由儿子来加载。 JVM的类加载器是有亲子层级结构的,启动类加载器最上层,扩展类加载器第二层, 应用程序类加载器第三层,自定义类加载器第四层。当应用程序类加载器需要加载一个类时, 他会先委派给自己的父类加载器去加载,最终传导到顶层的类加载器去加载,但是如果父类 加载器在自己负责加载的范围内,没找到这个类,那么就会下推加载权利给自己的子类加载器 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Hj99VrjF-1572591931618)(/Users/laiyanxin/Library/Application Support/typora-user-images/image-20191027211307179.png)]

8.如何对“.class”文件处理保证不被人拿到以后反编译获取公司源代码?

首先编译时,就可以采用一些小工具对字节码加密,或者做混淆等处理

现在有很多第三方公司,都是专门做商业级的字节码文件加密的,所以可以付费购买他们的产品。

然后在类加载的时候,对加密的类,考虑采用自定义的类加载器来解密文件即可,这样就可以保证你的源代码不被人窃取。

9.什么是JVM的内存区域划分?

1.方法区

主要是放从“.class”文件里加载进来的类,还会有一些类似常量池的东西放在这个区域里。JDK 1.8以后叫“Metaspace”

2.执行代码指令用的程序计数器

记录当前线程执行的字节码指令的位置。每个线程都会有自己的一个程序计数器,专门记录当前这个线程目前执行到了哪一条字节码指令了

3.Java虚拟机栈

​ 来保存每个方法内的局部变量等数据的,这个区域就是Java虚拟机栈,每个线程都有自己的Java虚拟机栈,如果线程执行了一个方法,就会对这个方法调用创建对应的一个栈帧

​ 栈帧里就有这个方法的局部变量表 、操作数栈、动态链接、方法出口等东西。

4.Java堆内存

存放我们在代码中创建的各种对象的

5.本 地方法栈

native方法

10.Tomcat这种Web容器中的类加载器应该如何设计实现?

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Q3cwKqsT-1572591931619)(/Users/laiyanxin/Library/Application Support/typora-user-images/image-20191027212610692.png)]

每个WebApp负责加载自己对应的那个Web应用的class文件,也就是我们写好的某个系统打包好的war包中的所有class文件,不会传导给上层类加载器去加载。

11.对象创建过程,对象在Java堆内存里会占用多少内存空间呢?

1.对象创建过程
1. 平时我们都是通过new关键字来创建对象的,当虚拟机遇到一条new的指令的时候,首先会去检查这个指令参数是否会在常量池中定位到一个类的符号引用,并且检查这个符合引用代表的类是否被加载,解析和初始化,如果没有,则必须先执行相对应的加载。 2. 在类加载过后,虚拟机为新生对象分配内存,对象所需内存在类加载过后就可以确定,为对象分配空间有两种方式:第一种是**指针碰撞法**,如果JAVA堆是绝对完整的,所有用过的内存都放在一边,空闲的放在另一边,中间放着一个指针作为分界点的指示器。 这种方式适合内存是完整的,垃圾回收器要带有压缩功能,第二种是**空闲列表**,假设堆中的内存不是完整的,已使用的内存和空闲的内存相互交错,就没有办法指针碰撞了,虚拟机就必须维护一个列表,记录哪些空间是可以用的,在分配的时候从列表中找到一块足够大的空间分配给对象实例,并在更新列表记录。 3. 对象创建在并发环境下也不是线程安全的,对象A在分配内存空间,指针还没来的及修改,对象B又在原来指针空间分配内存,这个时候就有线程安全问题,解决这个问题有两种方案,第一种是虚拟机采用CAS配上失败重试的方式保证操作的原子性,第二种是TLAB(Thread Local Allocation buffer),是把内存分配的动作按照线程划分在不同空间中进行,既每个线程在JAVA堆中预先分配一小块内存,称为本地线程分配缓冲。哪些线程要分配空间就在哪个线程的TLAB上分配,只有TLAB分配完才能分配新的TLAB时,才需要同步锁定。 4. 分配完成后,虚拟机需要将分配到的内存空间都初始化为零,如果是TLAB这个工作是提前进行。这一步保证了对象实例字段在java代码中不被赋值就可以直接使用。 5. 对对象进行必要的设值,例如哪个对象是哪个类的实例,如果找到类元数据信息,hashcode,线程持有 锁等等 6. 上面工作完成了,就表示新的对象已经产生了,对象创建才开始,执行完new后会接着执行`<init>`方法,把对象进行初始化,这样一个对象才算产生出来。
2.对象在Java堆内存里会占用多少内存空间

对象对内存空间的布局可以分为:对象头+实例数据+对齐填充

对象头分为两部分组成:

一个是对象自己本身的一些信息 包括hashcode,GC分代年龄,线程持有锁,锁状态标志,偏向线程ID,偏向时间戳,被称为mark word类元数据指针 指向类元数据的指针,虚拟机根据这个指针确定哪个类的实例。并不是所有虚拟机都保留类型指针。如果是数组,对象头还必须有一块记录数组的长度,因为虚拟机可以通过普通对象的元数据信息确定对象的大小,而数据的元数据是无法确定大小的。

实例数据:代码中定义的类型字段内容

对齐填充:不是必然存在,对象必须是8字节的倍数,如果对象实例数据没有对齐,则通过对齐填充来补全。

3.对象访问定位

对象访问方式取决于虚拟机实现而定,目前主要的方式是使用句柄和直接引用。

使用句柄就是将java堆中划分一块内存为句柄池,reference中存储的就是对象的句柄地址,而句柄中包含了对象的实例数据和类型数据各自的具体地址信息。

直接引用就是reference中存储的就是对象地址

这两种方式各有优势,使用句柄最大好处是稳定的句柄地址,对象移动只改变句柄中的实例数据地址,而引用变量不需要改。缺点浪费空间。直接引用访问方式就是速度块,节省了内存。 这两种方式各有优势,使用句柄最大好处是稳定的句柄地址,对象移动只改变句柄中的实例数据地址,而引用变量不需要改。缺点浪费空间。直接引用访问方式就是速度块,节省了内存。

12.不再需要的那些对象应该怎么处理?

JVM的垃圾回收机制

​ JVM本身是有垃圾回收机制的,他是一个后台自动运行的线程,你只要启动一个JVM进程,他就会自带这么一个垃圾回收的后台线程。这个线程会在后台不断检查JVM堆内存中的各个实例对象,已经没有任何方法的局部变量在引用这个实例对象了他就会被回收

最新回复(0)