Jvm初学习01
JVM的位置
java程序跑在jvm上面,在操作系统之上最底下是硬件系统。如图所示
JVM的体系结构
jvm调优说的是“方法区”和“堆“,而”栈“、”本地方法栈“、”程序计数器“不存在垃圾回收一事
-虚拟机试图使用最大内存为电脑内存的1/4, 而jvm初始化内存为1/64(**-Xms1024m -Xmx1024m -XX:+PrintGCDetails**)
类加载器
-
定义: 类的加载指的是将类的.class文件中的二进制数据读入到内存中,将其放在运行时数据区的方法区内,然后在堆区创建一个 java.lang.Class对象,用来封装类在方法区内的数据结构
-
作用:加载类文件,引用在栈中,具体实例在堆中。
加载器种类
系统提供了 3 种类加载器:
启动类加载器(Bootstrap ClassLoader): 负责将存放在 <JAVA_HOME>\lib 目录中的,并且能被虚拟机识别的(仅按照文件名识别,如 rt.jar,名字不符合的类库即使放在 lib 目录中也不会被加载)类库加载到虚拟机内存中。
扩展类加载器(Extension ClassLoader): 负责加载 <JAVA_HOME>\lib\ext 目录中的所有类库,开发者可以直接使用扩展类加载器。
应用程序类加载器(Application ClassLoader): 由于这个类加载器是 ClassLoader 中的 getSystemClassLoader() 方法的返回值,所以一般也称它为“系统类加载器”。它负责加载用户类路径(classpath)上所指定的类库,开发者可以直接使用这个类加载器,如果应用程序中没有自定义过自己的类加载器,一般情况下这个就是程序中默认的类加载器。
三种类加载器的加载顺序:
Bootstrap ClassLoader > Extention ClassLoader > Appclass Loader
当然,如果有必要,还可以加入自己定义的类加载器。
双亲委派机制
- Java类加载机制,你理解了吗
- 面试官:java双亲委派机制及作用
- 当某个类加载器需要加载某个
.class
文件时,它首先把这个任务委托给他的上级类加载器,递归这个操作,如果上级的类加载器没有加载,自己才会去加载这个类。
作用
1、防止重复加载同一个.class。通过委托去向上面问一问,加载过了,就不用再加载一遍。保证数据安全。
2、保证核心.class不能被篡改。通过委托方式,不会去篡改核心.class,即使篡改也不会去加载,即使加载也不会是同一个.class对象了。不同的加载器加载同一个.class也不是同一个Class对象。这样保证了Class执行安全。
工作过程
如果一个类加载器收到了类加载的请求,它首先不会自己去尝试加载这个类,而是把这个请求委派给父类加载器去完成,每一个层次的类加载器都是如此,因此所有的加载请求最终都应该传送到顶层的启动类加载器中,只有当父加载器反馈自己无法完成这个加载请求(找不到所需的类)时,子加载器才会尝试自己去加载。
在 java.lang.ClassLoader 中的 loadClass 方法中实现该过程。
流程图(理解,向上委托,向下加载)
沙箱安全机制
Java安全模型的核心就是Java沙箱(sandbox),什么是沙箱?沙箱是一个限制程序运行的环境。沙箱机制就是将 Java 代码限定在虚拟机(JVM)特定的运行范围中,并且严格限制代码对本地系统资源访问,通过这样的措施来保证对代码的有效隔离,防止对本地系统造成破坏。沙箱主要限制系统资源访问,那系统资源包括什么?——CPU、内存、文件系统、网络。不同级别的沙箱对这些资源访问的限制也可以不一样。
所有的Java程序运行都可以指定沙箱,可以定制安全策略。
Native关键字
1、一个native方法就是一个Java调用非Java代码的接口。一个native方法是指该方法的实现由非Java语言实现,比如用C或C++实现。
2、在定义一个native方法时,并不提供实现体(比较像定义一个Java Interface),因为其实现体是由非Java语言在外面实现的
主要是因为JAVA无法对操作系统底层进行操作,但是可以通过JNI(java native interface java本地方法接口)调用其他语言来实现底层的访问。
举例:Thread类中的start() 方法中调用一个start0()的native方法。
- 调用底层c,c++语言库
- native -> jni -> 本地方法接口 -> 本地方法库
- 本地方法栈
- 一般用的不多,硬件开发用的多
PC程序计数器
- pc寄存器
- 程序计数器Program Counter Register
- 每个线程都有一个程序计数器,线程私有,就是一个指针,指向方法区中的方法字节码(用来存储指向下一条指令的地址, 也即即将要执行的指令代码)
- 非常狭小的空间 --可以忽略不计
方法区
从线程共享与否的角度来看
栈、堆、方法区的交互关系
方法区的基本理解
1:方法区(Method Area)与Java堆一样,是各个线程共享的内存区域。
方法区在JVM启动的时候被创建,并且它的实际的物理内存空间中和Java堆区一样都可以是不连续的。
2:方法区的大小,跟堆空间一样,可以选择固定大小或者可扩展。
3:方法区的大小决定了系统可以保存多少个类,如果系统定义了太多的类,导致方法区溢出,虚拟机同样会抛出内存溢出错误:java.lang.OutOfMemoryError: PermGen space 或者java.lang.OutOfMemoryError: Metaspace
加载大量的第三方的jar包;Tomcat部署的工程过多(30~50个);大量动态的生成反射类
关闭JVM就会释放这个区域的内存。
方法区(Method Area)存储什么?
方法区中存放:类信息、常量、静态变量、即时编译器编译后的代码。常量就存放在运行时常量池中。
当类被 Java 虚拟机加载后, .class 文件中的常量就存放在方法区的运行时常量池中。而且在运行期间,可以向常量池中添加新的常量。如 String 类的 intern() 方法就能在运行期间向常量池中添加字符串常量。