网上关于JVM的文章太多了,很难消化,凭自己的理解,阐述下。
JVM概述
JVM=java virtual machine。
主要作用:java文件编译后生成 . class文件(字节码),然后class文件通过JVM生成具体平台下的机器指令,实现java的平台无关性。
JVM= 类加载器classloader +运行时数据区域runtime data area + 执行引擎execution engine
运行时数据域 runtime data area = 方法区+堆内存+虚拟机栈+本地方法栈+程序计数器
执行引擎 = JIT 编译器+GC
classloader 把class文件加载到JVM中的运行时数据区域,接着由执行引擎来负责执行。
classloader
类加载器,主要是加载类。
默认三个类加载器: Bootstrap class loader(所有类加载器的父类)、extension class loader、system Class loader(也叫 application class loader)。
bootstrap:负责加载核心类库。jre/lib/rt.jar 中的jdk文件
extension:加载除了核心类库以外的拓展类。jre/lib/ext目录,java.ext.dirs指向的目录
system:负责加载classpath中指定的jar包以及目录中的class, classpath环境变量通常由-classpath或者-cp命令行选项定义
三个特性:单一性、可见性、委托性
一、单一性:父类加载过的类,子类不会二次加载
二、可见性:子类加载器可以看到父类加载的类,反之不行。
三、委托性:双亲委派模型。除了顶层bootstrap启动类加载器外,其他类加载器都有自己的父类加载器。
1、当前classloader首先从自己已经加载过的类中查询是否此类已经加载,如果已经加载就直接返回。每个类加载器都有自己的加载缓存,当一个类被加载了以后就会放入缓存,等下次加载的时候就可以直接返回。
2、当前classloader的缓存中没有找到被加载的类,就会委托父类加载器去加载,以此类推,一直到bootstrap类加载器。
3、当所有的父类加载器都没有加载的时候,就由bootstrap加载,加载失败,就有由extension加载,还失败,就由application加载。最后一直到当前的类加载器加载,并放入自己的缓存中。
个人理解,这种加载机制,目的:
1、为了安全性,避免自己编写的类动态替换了java的核心类,经常面试问到自己写的String类能代替java内置的String吗?
2、避免了重复加载。同一个class文件如果被不同classloader加载,就是不同的两个类。
runtime data area
JVM运行时数据区,经常问到,就是指jvm运行期间,对jvm内存空间的划分和分配。
1、PC程序计数器
2、java虚拟机栈。线程私有,每个方法被执行的时候会压入一个栈帧,包含局部变量表、操作数栈、动态链接、方法出口等
3、本地方法栈。用于本地方法
4、堆。所有线程共享,存储实例对象、数组值,基本上所有的new对象创建的对象内存都在此分配。新生代(eden区,from survior和to survior),老年代
5、方法区。所有线程共享,用于存储每一个类的结构信息,例如运行时常量池,成员变量和方法数据,构造函数和普通函数的字节码内容,还包含了一些在类、实例、接口初始化时用到的特殊方法。java反射用到的getName,isInstance等方法都是取自方法区。方法区对应permanent Generation持久代,用XX:PermSize指定大小
GC
java的垃圾回收机制,Garbage Collection。
主要的几个算法
1、标记-清除算法。速度快,占空间小,清除后会产生大量碎片。
2、复制算法。需要空间大,效率低,不会产生碎片。
3、分代搜集算法。
GC主要的活动区域在堆。
新生代大小 eden区:From survior:To survior = 8:1:1
为什么是8:1:1? 有人做个测试,GC大概90%的对象都会被清除,剩下百分之10%。
写的不是很好,错误的地方希望指点