一、JVM的位置
JDK JRE JVM三者的关系
- JVM=Java 运行器;
- JRE=JVM + Java 基础&核心类库;(如果不需要开发只是运行java程序,可以只安装JER)
- JDK=JRE + Java 开发工具(编译器、调试器等)。
二、JVM体系结构
1.java编译成class文件
执行命令javac HelloWorld.java会在目录下生成一个HelloWorld.class文件。
2.类加载器
1.作用
加载class文件,当我们有一个.class文件传入到类加载器中,会加载,初始化成为一个CLASS,之后我们就可以new出来一个实例对象,通过实例方法getClass获取到这个class,这里可以理解到类是模板,对象是具体实现。再通过class(类)的方法getClassLoaber获取类加载器。
2.加载器的类型
1.虚拟机自带的加载器
2.启动类(根)加载器
3.拓展类加载器
4.应用程序(系统)加载器
Class<?> aClass = Class.forName("org.example.bean.User");
ClassLoader classLoader = aClass.getClassLoader();
System.out.println("User的类加载器" + classLoader);//AppClassLoader //自定义
System.out.println("User的夫类加载器" + classLoader.getParent());//ExtClassLoader 位置信息jre1.8.0_181\lib\ext
System.out.println("User的祖父类加载器" + classLoader.getParent().getParent());//null 1.不存在,2.java找不到(不是用java语言编写的)位置信息jre1.8.0_181\lib\rt.jar
3.加载过程
1.加载器收到类加载请求。
2.将这个请求委托给父级处理,一直委托到启动类(根)加载器。
3.启动类(根)加载器检测看是否能加载,能加载就有当前加载器。
4.找不到抛出异常,通知子类加载。
5.重复3、4操作。
6.如果都找不到会出现classNotFount异常。
4.native关键字
public static void main(String[] args) throws Exception {
new Thread(new Runnable() {
@Override
public void run() {
start0();
}
}).start();
}
//native 出现的时间表示的是java作用范围不够,需要调用C语言逻辑,也就是本地方法库JNI
//出现native会进入本地方法栈,
//调用本地方法接口,JNI
//JNI作用拓展java的使用,融合不同的编程语言
public static native void start0();
3.PC寄存器(程序计数器)
每一个线程都会产生一个程序计数器,线程私有,就是一个指针,指向方法区中的方法字节码(用来存储一条指令地址,也就是即将要执行的指令代码),指令非常小,可以忽略不记。
4.方法区(Methon Area)
1.方法区释意
各个线程共享的内存区域。方法区在JVM启动的时候被创建,并且它的实际的物理内存空间中和Java堆区一样都可以是不连续的。方法区的大小,跟堆空间一样,可以选择固定大小或者可扩展。方法区的大小决定了系统可以保存多少个类,如果系统定义了太多的类,导致方法区溢出,虚拟机同样会抛出内存溢出错误:java.lang.OutOfMemoryError: PermGen space 或者java.lang.OutOfMemoryError: Metaspace
2.常量区
java中的常量池,通常指的是运行时常量池,它是方法区的一部分,一个jvm实例只有一个运行常量池,各线程间共享该运行常量池。
java常量池中保存了一份在编译期间就已确定的数据。它里面包括final常量的值(包括成员常量、局部常量和引用常量)、以及对象字面量的值。
在编译期间,每当给常量赋值它就会去检测常量池中是否存在该值,若存在直接返回该值的地址给常量,若不存在则先在常量池中创建该值,再返回该值的地址给常量。因此常量池中不可能出现相等的数据。
3.主要存储的数据
静态变量(static)、常量(final)、类信息(class)(构造方法、接口定义)、常量池;
**注意:**实例变量存在堆空间
5.栈(先进后出,后进先出)
栈内存:主线程的程序运行,生命的周期和线程同步。线程结束,栈内存释放,不会存在垃圾回收问题。
栈存储数据:8大基础类型+对象引用+实例方法
栈运行原理:栈帧,一开始stack2进入之后next调用stack1,当stack1结束之后弹出,子帧消失,之后stack2执行结束弹出,栈内存释放。当我们两个栈帧相互调用就会出现OOM。
6.堆
1.三种虚拟机:
sun公司:HotSpot
oracle公司:JRockit
IBM公司:J9JVM
查看虚拟机类型
2.存储数据
类进入类加载器之后,产生的类、方法、变量、常量
3.堆细分三个区域
1.新生区 老年区 数据流转
1.伊甸园区以及幸存者from区,以及幸存者to区。
2.当我们伊甸园区内存满了之后,会触发轻GC(垃圾回收)。清理不掉的会进入幸存者from区。之后幸存者from区和幸存者to区中幸存的数据会无限流转。
3.当幸存者区满了之后会触发轻GC(垃圾回收),把幸存者放入老年区。
4.当我们老年区满了之后,触发重GC。
5.当满了无法清理之后,也就是内存溢出OOM;
2.永久区(元空间)
这个区域一直存在在内存中,携带JDK自带的类对象、Interface元数据、java运行环境、类信息。这个区域没有垃圾。关闭虚拟机释放内存。
JDK1.6之前:永久代,常量池在方法区
JDK1.7:永久代,但是慢慢退化,常量池在堆中
JDK1.8之后:无永久代,常量池在元空间
问题OOM问题:
1.一个启动类,加载许多第三方Jar包;
2.一个tomcat启动多个服务;
3.大量反射类,不断加载