灵魂拷问JVM,你被干趴了吗

大家好,我是小轩,Java内存模型在面试过程中必不可少,最近也是整理了一些笔记。

Java的类加载过程

jvm将.class类文件信息加载到内存并解析成对应的class对象的过程,注意:jvm并不是一开始就把所有的类加载进内存中,只是在第一次遇到某个需要运行的类才会加载,并且只加载一次

主要分为三部分:

1、加载,2、链接(1.验证,2.准备,3.解析),3、初始化

一、加载

1、类加载器

上面已经提到了类加载的过程是由类加载来完成,Java内部提供了几种类加载器:

启动类加载器(Bootstrap Class Loader)

扩展类加载器(Extension Class Loader)

应用程序类加载器(Application Class Loader)

① 启动类加载器(Bootstrap Class Loader)

负责加载存放在JRE的lib目录下的核心类库,比如rt.jar、charsets.jar等。

② 扩展类加载器(Extension Class Loader)

主要负责加载存在在JRE的lib/ext目录下的JAR包以及通过java.ext.dirs系统变量指定的路径中的JAR包。

③ 应用程序类加载器(Application Class Loader)

主要负责加载ClassPath路径下的所有JAR包。

开发人员还可以通过自定义类加载器来进行扩展。

2、双亲委派机制

Java中类加载器之间的关系通过双亲委派机制来保证

b49bae9f82c66fa0170e1e8876e3e14c.png

双亲委派机制确保在加载每个类时,由当前的类加载器委派给它的父(上层)类加载器加载,依次类推直到启动类加载器,如果启动类加载器在加载的路径下找不到目标类,则再交给子(下层)类加载器加载,同样依次类推,如果所有的父加载器都找不到目标类,则在当前类加载器的路径中查找并加载目标类。

简单来说就是父加载器优先加载,父加载器无法加载再由子加载器加载。

好处就是可以避免重复加载。

二、链接

验证:(验证class文件的字节流是否符合jvm规范)

准备:为类变量分配内存,并且进行赋初值

解析:将常量池里面的符号引用(变量名)替换成直接引用(内存地址)过程,在解析阶段,jvm会把所有的类名、方法名、字段名、这些符号引用替换成具体的内存地址或者偏移量。

三、初始化

主要对类变量进行初始化,执行类构造器的过程,换句话说,只对static修试的变量或者语句进行初始化。

范例:Person person = new Person();为例进行说明。

Java编程思想中的类的初始化过程主要有以下几点:

1. 找到class文件,将它加载到内存

2. 在堆内存中分配内存地址

3. 初始化

4. 将堆内存地址指给栈内存中的p变量

上面的过程发生在Java虚拟机中

d37b1383b75cb7dd64c3503308d04fb2.png

Java虚拟机一句话说就是:Java 虚拟机就是我们java应用的运行环境,Java语言使用Java虚拟机屏蔽操作系统和机器之间的差异,达到Write Once Run Anywhere。

通常由javac 去编译程序源代码,转换成Java字节码,JVM通过解析字节码文件,将其翻译成对应的机器指令,逐条读入,逐条解析。

JVM整体结构

2a6823494c83e23aa075c5749bd8a49f.png

JVM称为Java虚拟机,它要对应用程序的线程等分配内存空间,还要负责加载类、解析执行编译后的字节码文件,所以整个JVM主要就包含了运行时数据区、类加载子系统和字节码执行引擎。

运行时数据区是在内存中的,所以这一部分也成为JVM的内存模型。这里要与JMM(Java内存模型)区分开。

内存模型主要包含五部分的内容:堆、栈、本地方法栈、方法区(元空间)、程序计数器。

堆:JVM管理的最大一块内存空间,它是所有线程所共享的一块区域。在虚拟机启动的时候创建,该区域的唯一目的就是为了存放对象实例。

栈(虚拟机栈):也可以称为虚拟机线程栈,它是JVM中每个线程所私有的一块空间,每个线程都会有这么一块空间。虚拟机栈描述了Java中方法执行时的内存模型,即每个方法被执行的时候,线程都会在自己的线程栈中同步创建一个栈帧(Stack Frame),用于存放局部变量表、操作数栈、动态连接和方法出口等信息,每个方法从调用到完成的过程,就对应着一个栈帧在线程栈中从入栈到出栈的过程。

本地方法栈:本地方法栈与虚拟机栈的作用是相似,不同的是虚拟机栈为JVM执行的Java方法服务,而本地方法栈为JVM调用的本地方法服务。

方法区:在JDK 8之前,方法区也称之为永久代,这部分区域与堆一样,是所有线程所共享的,它主要用于存放被虚拟机加载的类型信息、常量、静态变量以及即时编译器编译后的代码缓存等数据。

程序计数器:每个线程都会有自己独立的程序计数器,主要功能就是记录当前线程执行到哪一行指令了,只需要一小块内存空间。

JVM内存参数

dd8cc75189c518bb064ec797d652286e.png

JVM内存模型中的这些区域,都是有大小限制的,当然也可以通过JVM提供的参数来设置这些区域所占内存的大小。

下面介绍这些常用参数的含义:

-Xss:表示每个线程栈的大小

-Xms:表示堆空间的初始可用大小,默认为物理内存的1/64

-Xmx:表示堆空间的最大可以大小,默认为物理内存的1/4(这个参数在应用程序中是一定要指定的)

-Xmn:表示新生代(年轻代)的大小

-XX:NewRatio:默认为2,表示新生代占年老代的1/2,占整个堆内存的1/3。

-XX:SurvivorRatio:默认为8,表示一个survivor区占用1/8的Eden内存,即1/10的新生代内存。

-XX:MaxMetaspaceSize:设置元空间最大值, 默认是-1, 即不限制, 或者说只受限于本地内存大小。

-XX:MetaspaceSize:指定元空间触发Full Gc的初始阈值(元空间无固定初始大小), 以字节为单位,默认是21M左右,达到该值就会触发full gc进行类型卸载, 同时收集器会对该值进行调整:如果释放了大量的空间, 就适当降低该值;如果释放了很少的空间, 那么在不超过-XX:MaxMetaspaceSize(如果设置了的话) 的情况下, 适当提高该值。

由于调整元空间的大小需要Full GC,这是非常昂贵的操作,如果应用在启动的时候发生大量Full GC,通常都是由于永久代或元空间发生了大小调整,基于这种情况,一般建议在JVM参数中将MetaspaceSize和MaxMetaspaceSize设置成一样的值,并设置得比初始值要大,对于8G物理内存的机器来说,一般推荐将这两个值都设置为256M。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值