java的跨平台性

在下载JDK的时候,是根据不同的操作系统下载不同的JDK版本,在JDK中,对于不同的操作系统,会用不同的JVM实现,JVM封装了底层对于不同操作系统的指令等,因此对于开发人员来说,不需要修改代码就可以在不同的系统上运行
image.png

JVM整体结构及内存模型

  1. ![image.png](https://cdn.nlark.com/yuque/0/2022/png/25761560/1641732676814-595a98f7-2ea5-4f9c-98d1-e2bb608a794b.png#clientId=u94e629f2-fb87-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=418&id=u99ca4321&margin=%5Bobject%20Object%5D&name=image.png&originHeight=717&originWidth=766&originalType=binary&ratio=1&rotation=0&showTitle=false&size=60879&status=done&style=none&taskId=u541e52ef-3972-4926-a820-556a2efc8db&title=&width=447)<br />首先通过编译器把Java代码转换成字节码,类加载器(ClassLoader)再把字节码加载到内存中,将其放在运行时数据区(Runtime data area)的方法区内,而字节码文件只是JVM的一套指令集规范,并不能直接交给底层操作系统去执行,因此需要特定的命令解析器执行引擎(Execution Engine),将字节码翻译成底层系统指令,再交由CPU去执行,而这个过程中需要调用其他语言的本地库接口(Native Interface)来实现整个程序的功能。

运行时数据区(JDK8,元空间代替永久代):

1.程序计数器(无GC,无OOM)

CPU只有把数据装载到寄存器才能够运行
作用:程序计数器(寄存器)用来存储指向下一条指令的地址,也即将要执行的指令代码。由执行引擎读取下一条指令并执行。也是唯一一个在JVM中没有任何OOM情况的区域
image.png

2.Java栈(虚拟机栈,无GC)

栈是运行时的单位,而堆是存储的单位。
作用:保存方法的局部变量(8种基本数据类型,对象的引用地址),部分结果,并参与方法调用和返回。
一个栈帧(stack frame)对应一个方法,存储着:
a.局部变量表(局部变量数组或本地变量表)
b.操作数栈(或表达式栈) FILO,主要用于保存计算过程的中间结果,同时作为计算过程中变量临时的存储空间
c.动态链接(或指向运行时常量池的方法引用) 内存地址
d.方法返回地址
e.一些附加信息
image.png

3.堆(GC的重点区域)

Java7之前:年轻代(Eden,Survivor)+老年代+永久代
Java8之后:年轻代(Eden,Survivor)+老年代+元空间
image.png
对象的分配:首先会被分配到Eden区,放满后,开始垃圾回收(YGC/Minor GC),判断对象是否还在用,若还在用,将其移至S0,分代年龄加1,随后继续往Eden区放对象,满后连同S0一起YGC/Minor GC,若发现S0或Eden的对象还在用,将其放入S1,分代年龄加1,此时S0为空(为to区),也就是S区总有一个会为空(谁空谁就是to),当S区的年龄计数器达到阈值,移至老年代。
注:大对象直接分到老年代(大对象要避免出现)
关于GC:频繁在年轻代收集,很少在老年代收集,几乎不在永久代/元空间收集

4.方法区(有GC)

image.png 方法区(逻辑),永久代/元空间是对方法区的实现
方法区可以看作是一块独立于java堆的内存空间,元空间不在虚拟机设置的内存中,而是使用本地内存
方法区用于存储已被虚拟机加载的类型信息、域信息、方法信息、常量、静态变量,即时编译器编译后的代码缓存等。
类型信息:
对每个加载的类型(类class,接口interface,枚举enum,注解annotation),JVM必须在方法区中存储以下类型信息:
1.这个类型的完整有效名称(全名=包名.类名)
2.这个类型直接父类的完整有效名
3.这个类型的修饰符
4.这个类型直接接口的一个有序列表
域信息:域名城,域类型,域修饰符(public,private,protected,static,…….)
方法信息:局部变量表大小,操作数栈大小等等

HotSpot方法区变化:
jdk1.6及以前:有永久代,静态变量存放在永久代上。
jdk1.7:有永久代,但已经逐步“去永久代”,字符串常量池和静态变量移除,保存在堆中。
jdk1.8及以后:无永久代,类型信息、字段、方法、常量保存在本地内存的元空间,但字符串常量池和静态变量仍然在堆中。

5.本地方法栈

本地方法栈的功能和特点类似于虚拟机栈,均具有线程隔离的特点
不同的是,本地方法栈服务的对象是JVM执行的native方法,而虚拟机栈服务的是JVM执行的java方法

未命名文件 (2).png
注:上述图关于堆区域中存的Class和Class画的有些不规范,现已将规范的图列出如下:
022943b65585adeb094b10b786a10bf.png

JVM内存参数设置

       ![image.png](https://cdn.nlark.com/yuque/0/2022/png/25761560/1641820377296-65532be1-aaa9-42f2-94d6-d0415065b1cf.png#clientId=u0784145d-03c7-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=216&id=u3c5d5d6d&margin=%5Bobject%20Object%5D&name=image.png&originHeight=373&originWidth=608&originalType=binary&ratio=1&rotation=0&showTitle=false&size=50397&status=done&style=none&taskId=uaa56291b-793a-410a-8b4a-68776499a7f&title=&width=352)<br />关于元空间的JVM参数有两个:**-XX:MetaspaceSize=N和 -XX:MaxMetaspaceSize=N** <br />**-XX:MaxMetaspaceSize:** 设置元空间最大值, 默认是-1, 即不限制, 或者说只受限于本地内存大小。<br />**-XX:MetaspaceSize:** 指定元空间触发Fullgc的初始阈值(元空间无固定初始大小), 以字节为单位,默认是21M,达到该值就会触发full gc进行类型卸载,同时收集器会对该值**进行调整**:如果释放了大量的空间,就适当降低该值;如果释放了很少的空间,那么在不超过-XX:MaxMetaspaceSize(如果设置了的话)的情况下, 适当提高该值。这个跟早期jdk版本的**-XX:PermSize**参数意思不一样,**-XX:PermSize**代表永久代的初始容量。 

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

问题一:说一下JVM的主要组成部分以及作用

JVM包含两个子系统两个组件,两个子系统为Class loader(类装载子系统)、Execution engine(执行引擎);两个组件为Runtime data area(运行时数据区)、Native Interface(本地接口)。
Class loader(类装载子系统):根据给定的全限定名类名(如:java.lang.Object)来装载class文件到Runtime data area中的method area。
Execution engine(执行引擎):执行classes中的指令。
Native Interface(本地接口):与native libraries交互,是其它编程语言交互的接口。
Runtime data area(运行时数据区域):这就是我们常说的JVM的内存。
作用 :首先通过编译器把Java代码转换成字节码,类加载器(ClassLoader)再把字节码加载到内存中,将其放在运行时数据区(Runtime data area)的方法区内,而字节码文件只是JVM的一套指令集规范,并不能直接交给底层操作系统去执行,因此需要特定的命令解析器执行引擎(Execution Engine),将字节码翻译成底层系统指令,再交由CPU去执行,而这个过程中需要调用其他语言的本地库接口(Native Interface)来实现整个程序的功能。

问题二:JVM优化原则是什么?

尽可能让对象都在新生代里分配和回收,尽量别让太多对象频繁进入老年代,避免频繁对老年代进行垃圾回收,同时给系统充足的内存大小,避免新生代频繁的进行垃圾回收。

问题三:永久代和元空间的区别,为什么要用元空间代替永久代?

永久代主要占用还是JVM堆空间,一旦固定就变不了,碰到热加载就可能会出现OOM问题。
元空间使用的操作系统本地内存空间,而且可以动态扩展,默认可以是最大本地可用内存。如果设置了 maxMeataSpace,到达这个阈值会触发GC,可以回收掉无用的类信息。