1、Write once,Run Anywhere
Java在诞生之初就提出了“Write once,Run Anywhere”的口号,只要写了一份代码,就可以在多平台运行(windows、Linux),而能做到这一切的基础便是JVM,只需要在不同的操作系统上装好JDK(JAVA DEVELOPMENT KIT),这里的JDK是java语言、运行环境、核心类库的统称。
2、JDK
2.1、JDK体系结构
体系结构图如下所示:
主要分成了三大类:
JDK:Java Development Kit,在jre的基础上还包括了java语言以及一些java工具,提供编译、监视、调试等功能。
JRE:Java Runtime Environment,java的运行环境,包含了开发者环境,用户工具包,标准类库以及虚拟机等等。
JAVA SE API:主要指的是java官方提供的标准类和接口。
2.2、跨平台特性
我们编写好的java代码(.java)通过javac这个工具编译成JVM可识别的字节码文件(.class),再通过类加载器在JVM加载到内存中,在不同的平台JVM会自己翻译成对应的机器码,从而实现跨平台的功能。
3、整体结构与内存模型
3.1整体结构
大概的结构如下图所示:
3.2内存模型
java运行时内存主要分成了五个区,包括堆、栈、方法区、本地方法区、程序计数器。详细介绍在上一篇文章中有,地址:JVM性能调优_Arura_Owl的博客-优快云博客
下面结合代码一起理解一下这五个区:
public class Demo {
// 静态常量1
private static final int d = 11;
// 静态常量2
private static final int d = 11;
// 静态方法1
public static int childDemo(){
int a = 0;
int b = 1;
int c = 2;
int abc = a + b + c;
return abc;
}
// 静态方法2
private static final void finalTest(){
System.out.println("finalTest");
}
// main方法
public static void main(String[] args) {
int res = childDemo();
System.out.println(res);
}
}
在执行这个Demo类的main方法的时候,会编译然后加载这个文件,我们在加上断点后能看到目前进入到了主方法,等待执行第一句语句,此时的d和e已经有值了,因为在启动时,静态变量会直接加载进内存,这一部分数据是在堆中的,可以被其他线程共享使用。
继续向下走,进入childDemo子方法
到这一步时,又多了a和b的值,只有调用了childDemo这个方法时,方法里创建的变量赋值语句才会运行,而这一部分数据是存放在栈中,每一个方法以栈帧的形式按照先进后出的顺序存放在栈中。
当这个childDemo的生命周期结束时,a、b、c以及abc的生命周期也随着childDemo一起结束。
4、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。