Java有三种主要的类加载器:
1. 启动类加载器(Bootstrap Class Loader):它是Java虚拟机的一部分,负责加载Java的核心类库,如java.lang包中的类。它是用本地代码实现的,不继承自java.lang.ClassLoader。
2. 扩展类加载器(Extension Class Loader):它是由sun.misc.Launcher$ExtClassLoader实现的,负责加载Java的扩展类库,位于JRE的lib/ext目录下。
3. 应用程序类加载器(Application Class Loader):它是由sun.misc.Launcher$AppClassLoader实现的,也被称为系统类加载器。它负责加载应用程序类路径(classpath)下的类,包括开发者自定义的类。
除了这三种主要的类加载器,还有其他一些特殊的类加载器,如安全类加载器(Security Class Loader)和其他自定义的类加载器。这些类加载器可以根据需要进行扩展和定制,以满足特定的加载需求。
Class not found~
百 度 双亲委派机制
native关键字:
比如线程:start()方法里有个start0()它是native修饰的,它是java实现不了,需要调用本地方法,JVM在操作系统之上,用接口调用本地方法,想要调用底层需要用native关键字
JNI:java native interface (java本地接口)
- /**
- * native:凡是带了native 关键字的,说明java的作用范围达不到了,回去调用底层c语言的库!
- * 会进入本地方法栈
- * 调用本地方法本地接口 俗称 JNI java native interface
- * JNI作用:扩展Java的使用,融合不同的编程语言为Java所用! 最初:C,C++!
- * Java诞生的时候 C ,C++横行,想要立足,必须要有调用C,C++的程序~
- * 所以它在内存区域中专门开辟了一块标记区域:Native Method Stack。用于登记 native 方法
- * 在最终执行的时候,调用本地方法接口去找到本地方法库中的方法去加载
- *
- * Java程序驱动打印机,管理系统,掌握即可,在企业级应用中较为少见
- * Robot()这个类里就有大部分native方法,因为它是操作操作系统的。
- */
- private native void start0();
其实调用其他语言,有很多种方法,别学死了
调用其他接口:
举个例子
球球爱心网:-->输入(PHP) --> nodeJS-->Socket-->C++-->游戏送爱心
程序计数器:Program Counter Register
每个线程都有一个程序计数器,是线程私有的,就是一个指针,指向方法区中的方法字节码(用来存储指向下一条指令的地址,也是即将要执行的指令代码),在执行引擎读取下一条指令,是一个非常小的内存空间,几乎可以忽略不计。
这就是为什么线程有1,2,3号线程这一说法
Method Area 方法区
方法区是被所有线程共享,所有字段和方法字节码,以及一些特殊方法,如构造函数,接口代码也在此定义,简单说,所有定义的方法的信息都保存在该区域,此区域属于共享区间
静态变量、常量、类信息(构造方法、接口定义)、运行时的常量池存在方法区中,但是实例变量存在堆内存中,和方法区无关
Java字节码对象存储在内存中的方法区(Method Area)中。方法区是Java虚拟机(JVM)的一部分,用于存储类的结构信息,包括类的字节码、常量池、字段和方法信息等。在JVM启动时,方法区就会被创建,并且在整个程序运行期间都存在。
常量在Java中的存储位置取决于其类型。对于基本类型的常量(如整数、浮点数等),它们的值直接存储在方法区的常量池中。而对于引用类型的常量(如字符串、数组等),常量池中存储的是对象的引用,而对象本身则存储在堆内存中。
方法区的常量池是一块特殊的内存区域,用于存储类的常量、静态变量、字符串字面量等。常量池在编译阶段就被确定,并且在类加载时被加载到方法区中。由于常量的值在编译期间就确定了,而且在运行时不可修改,所以将常量存储在方法区是合理的选择。这样可以节省堆内存的空间,并且提高常量的访问效率。
static.final.class.常量池
调用静态方法时会将一个帧(frame)压入栈中。当调用静态方法时,会创建一个新的栈帧来存储局部变量、方法参数和其他执行该方法所需的数据。这个栈帧被推入栈中,一旦方法执行完成,该帧就会从栈中弹出。
画出一个对象实例化的过程在内存中:百度、看视频~
需要去查:如何栈里东西去存?细节?
这个区域是常驻内存的区域。用来存放JDK自身携带的Class对象。Interface元数据,存储的是Java运行时的一些环境或类信息~,这个区域不存在垃圾回收!关闭JVM虚拟机就会释放这个区域的内存~
一个启动类,加载了大量的第三方jar包。Tomcat部署了太多的应用,大量动态生成的反射类,不断的被加载,直到内存满,就会出现OOM Out of Memory;
方法区是一个特殊的堆
元空间和永久代是方法区具体的落地实现
所以常量池 静态变量 Class在元空间里面
元空间:在逻辑上存在,在物理上不存在,因为它本身就是堆空间的一部分
- package com.qf.jvmStudy.nativeDemo;
-
- public class Demo03 {
- public static void main(String[] args) {
- //返回虚拟机试图使用的最大内存
- long max = Runtime.getRuntime().maxMemory();//字节*1024*1024=MB
- //返回JVM的总内存
- long totalMemory = Runtime.getRuntime().totalMemory();
-
- //默认情况下:分配的最大内存 是电脑内存的1/4 ,而初始化的内存:1/64
- System.out.println("max=+"+max+"字节\t"+(max/(double)1024/1024)+"MB");
- System.out.println("totalMemory=+"+totalMemory+"字节\t"+(totalMemory/(double)1024/1024)+"MB");
- //虚拟机试图使用的最大内存 JVM实际用的总内存 打印GC细节信息
- //-Xmx1024m -Xms1000m -XX:+PrintGCDetails
-
- //OOM: 堆内存满了:我们先尝试把堆内存扩大,如果扩大同样的代码还报同样的错,分析内存,看一下哪个地方出现了问题(专业工具)
- // 305664K+699392K= 1,005,056k=981.5MB totalMemory=+1029177344字节 981.5MB
- }
-
- }
- package com.qf.jvmStudy.nativeDemo;
-
- import java.util.ArrayList;
- //-Xmx1m -Xms1m -XX:+HeapDumpOnOutOfMemoryError
- public class Demo04 {
- public static void main(String[] args) {
- ArrayList
demo04s = new ArrayList<>(); -
- try {
- while (true){
- demo04s.add(new Demo04());
- }
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
- }
项目上线看不到具体报错行,只能dump后进行分析
//-Xmx1024m -Xms1000m -XX:+PrintGCDetails //打印GC回收信息
//-Xmx1m -Xms1m -XX:+HeapDumpOnOutOfMemoryError //OOM :Dump
Runtime类,就是JVM用来调优的类,打印运行时的一些环境的一些东西
JVM在进行GC时,并不是对这三个区域统一回收。大部分的时候,回收都是新生代~
新生代(伊甸园)
幸存区(from区 to区)
老年区
GC两种类:轻GC(普通的GC) 重GC(全局的GC)
轻GC偶尔在幸存区清理,基本上在伊甸园
引用计数法
没讲全,引用计数法是对象被引用了计数器+1,引用结束-1,计数器为0就会被回收
新生代划分为3块区域Eden、From Survior、ToSurvior,内存比例为8:1:1。
minor GC不会OOM吧,只有Full GC也没法清理出足够内存响应申请才会OOM
碎片就是内存一块一块的
再优化:
再优化:每清除标记五次,标记压缩一次。
Java Memory Model(Java内存模型)
元空间不在JVM虚拟机内存里。他用的是本地内存。
第一、1.7版本永久代内存是有上限的,虽然我们可以通过参数去设定,但是JVM加载的Class总数大小是很难去确定的,所以很容易出现OOM的一个问题,但是元空间是存储在本地内存里面,内存上限是比较大的,可以很好的去避免这个问题。
第二、 永久代的对象 是通过FullGC进行垃圾回收的,也就是和老年代同时实现垃圾回收,替换成元空间简化了FullGC的这样一个过程,可以在不进行暂停的情况下,去并发的释放类的数据,同时也提升了GC的一个性能
第三、Oracle要合并Hotspot和JRockit的一个代码,而JRockit里没有永久代,更好的实现合并
单点登录~ 架构师