首先了解JVM架构:
CSDN
硬件决定执行性能:JDK1.8之前(1.8之后 JVN可以不完全受到硬件的性能制约)
类加载器ClassLoader 三层:
- BootStrap 系统类加载器,涉及JVM的本地实现,获取不到,加载JDK里的API,如java.* ,java.lang
- ExtClassLoader 扩展类加载器(D:\java\jdk1.8.0_251\jre\lib\ext目录下的 / 平台类加载器
- AppClassLoader 自定义类加载器
public class Test{
public static void main(String[] args){
//String 是一个系统类,系统类的类加载器是不一样的Bootstrop
String str = "hello";//静态常量池进行定义,因为字符串这个类型是我们开发中用的非常多的一个数据类型
System.out.println(str.getClass().getClassLoader());//null
Member member = new Member();
//AppClassLoader 自定义类加载器
System.out.println(member.getClass().getClassLoader());
//ExtClassLoader扩展类加载器 jdk/jre/lib/ext/...
System.out.println(member.getClass().getClassLoader().getParent());
//BootStrap 系统加载器无法获取,由JDK实现
System.out.println(member.getClass().getClassLoader().getParent().getParent());//null
}
}
class Memeber{}
编译:
从上往下执行,先从系统类库(JDK)中查找这个类,再往下一致查找classpath,若没有,则会抛出编译异常
执行:
自低向上查找,先从classpath中查找,若没有,则继续从父加载器中查找,直到Bootstrap,若找不到,则会抛出ClassNotFoundException异常
双亲委派机制(自低向上查找时出现): 系统类由系统类加载器负责,而自定义类由其它的类加载器负责
双亲委派机制作用:
1)防止类的重复加载(如果calsspath中找到就不会在往上查找)
2)安全(防篡改字节信息)
不同的类加载器加载的类的信息,在内存中是不一样的,比如:系统自带String和自己写的String类加载在内存中的内容是不一样的。
类加载的字节码文件会加载到运行时数据区里。
运行时数据区内部结构:
CSDN
执行引擎负责执行运行时数据区加载的函数,JNI接口包括本地函数库的调用。Java没有C、C++运行快的原因:Java面向虚拟机的指令编程,而不是面向软件的应用编程。
JVM调优:
1,尽量减少GC的操作;
2,参数调优
运行时数据区(内存问题)?
深颜色的是方法区和堆是线程共享区,浅色的栈内存、程序计数器和本地方法栈是线程私有区。
1)方法区:最重要的一个内存区域,多线程共享,保存了类的信息(类名、成员、接口、父类),反射机制是重要的组成部分,动态的进行类操作的实现
2)堆内存(Heap):解决的是数据存储的问题,保存对象的真实信息,该内存牵扯到释放问题(GC):
CSDN
3)栈内存(Stack):解决程序的运行问题,即程序如何执行,或者说如何处理数据。线程的私有空间,在每一次进行方法调用的时侯都会存在栈帧,采用先进后出的设计原则。
栈帧:
-
本地变量表:局部参数或者形参,允许保存32位的插槽(Solt),如果超过32位的长度就需要开辟两个连续性的插槽(long,double)–volatile关键字问题
-
操作数栈:执行所有的方法计算操作
-
常量池引用:String类实例等
-
返回地址:方法执行完毕后的恢复执行的点
4)程序计数器:执行指令的一个顺序编码,该区域的所占比率几乎可以忽略
5)本地方法栈:与栈内存功能相似,区别在于是为本地方法(JNI)服务的
举例:
User user = new User();
- User 类信息会存在方法区中
- User的实例user,存储在Java堆内存中
- 使用的都是User对象的引用(指针)
public static void fun(){
fun();
}
注意:没有退出条件,会抛出异常 :StackOverError ,每调用一次方法就会在栈里产生一个栈帧,当栈帧满了就会抛出此异常
版权声明:本文为weixin_40959890原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。