从上面的图可以看到,包含Class文件,类加载器(classloader),内存空间(分为方法区,java堆区,java栈区,本地方法区,垃圾收集),执行引擎,本地方法接口。
编译:
Java源代码是不能被机器识别的,需要经过编译器变异成JVM虚拟机可执行的.class字节码文件,在由解释器解释运行。把.java文件编译成.class文件。上面图其实就是javac命令根据不同的解析语法来解析java文件。
使用Classloader或者它的子类加载编译好的文件,Java中主要包含下面几中加载器
Bootstrap ClassLoader: 根ClassLoader,用C++实现,专门用来加载Java的核心API:$JAVA_HOME中jre/lib/rt.jar中所有class文件rt的意思是runtime
Extension ClassLoader: 加载Java扩展API jre/lib/ext中的类
App ClassLoader: 加载classpath目录下定义的class,也就是应用程序用到的ClassLoader。
Custom ClassLoader: 可以自定义的ClassLoader,可以继承这个ClassLoader然后自己实现。
类的加载过程如下
加载:把.class类的信息从文件中获取并且载入到JVM内存里
验证:检查读入的字节码是否符合JVM规范
准备:分配一个数据结构来存储类的信息,为静态变量分配内存,将其初始化为默认值。
解析:把这个类的常量池中的所有的符号引用改变成直接引用
初始化:执行静态初始化程序,把静态变量初始化成指定的值
JVM执行引擎执行字节码
垃圾回收主要针对堆内存部分,按照分代算法划分,堆内存空间可以分为年轻代和老年代。
刚创建的对象会放到年轻代,年轻代的对象一般会在下一次垃圾回收的时候被回收,多次GC都没有被回收的对象就进入到了老年代
我们可以根据业务动态的调整年轻代和老年代的区域的大小。
引用计数算法:
给每个对象分配一个引用计数器,每当有地方引用该对象的时候,引用计数器的值加一,当引用失效的时候,引用计数器减一,如果引用计数器为0说明该对象不在被引用了
缺点:如果两个对象之间互相引用,比如A引用B,B引用A,别的对象都没有引用A或者B,这时候他们俩其实是垃圾了,但是他们的引用计数器不为0,计数器也就无法通知GC回收他俩了。
可达性算法(java1.2之后):
通过一些被称为引用链(GC Roots)的对象作为起点,从这些节点开始往下搜索,搜索走过的路程叫做引用链(Reference Chain),如果一个对象到GC Roots没有任何引用链接,说明这个对象不可用了。
Java中可以被称为GC Root的对象可以是一下几种:
(1)虚拟机栈(栈帧中的本地变量表)中引用的对象
(2)方法区中静态属性引用的对象
(3)方法区中常量引用的对象
(4)本地方法中Native方法引用的对象
上面两种算法中都有引用这个概念,Java中的引用有四种
(1)强引用,new出来的对象比如Object obj = new Object()的引用。垃圾回收器永远不会收集被强引用的对象。
(2)软引用,一些有用但不是必须的对象Java中使用java.lang.ref.SoftReference来表示,在内存不足的时候会回收改对象
(3)弱引用,非必须对象,当垃圾回收的时候无论内存是否充足,都会回收被弱引用挂你蓝的对象,Java中使用java.lang.ref.WeakReference这个类来表示
(4)虚引用,任何时候都可以被回收,Java中使用java.lang.ref.PhantomReference类表示。用的不多。
1.标记清除算法
图片是从网上寻找的
从根节点开始遍历所有的引用,未被引用的对象标记为可回收对象,清除可回收对象
优点:不需要对象的移动,直接删除未被引用的对象,在对象较少的时候效率高
缺点:由于直接删除对象,会造成内存碎片
2.复制算法
图片是从网上寻找的
找一块空闲内存,从根节点开始遍历,把可以引用的对象复制到新的内存块中,然后清除原来内存块中的所有对象
优点:不会出现内存碎片
缺点:需要一块内存作为交换空间来移动对象,空间成本较高
3.标记整理算法
图片是从网上寻找的
从根节点开始遍历标记有引用的对象,未被标记的就是需要回收的,删除掉需要回收的对象之后,把所有对象压缩到内存的一端
优点:不会产生内存碎片
缺点:需要进行对象的移动,时间成本较高。