一、Java文件编译的过程
- 程序员编写.java文件
- javac编译成字节码文件.class(JVM只认识.class文件)
- 在由JVM解释成电脑认识的机器语言
二、JVM、JRE、JDK
JVM:Java虚拟机,运行Java字节码文件.calss的虚拟机。
Java针对不同的系统平台都开发了对应的虚拟机,实现跨平台,一次编写,多处运行。JVM使用GC垃圾回收器和自适应优化器。
JRE:Java运行环境是一个软件,JRE为Java运行提供了环境。内部有一个Java虚拟机,以及一些标准的类库(Class Library)。
JDK:Java软件开发工具包,用于各种环境下的Java程序开发。是整个Java开发的核心,包含了JRE(JVM+Java类库Java API)和Java工具(Javac)。
JDK目录下有四个文件夹:bin、include、lib、 jre。
bin:最主要的是编译器(javac.exe)
include:java和JVM交互用的头文件
lib:类库
jre:java运行环境
总的来说JDK是用于java程序的开发,而jre则是只能运行class而没有编译的功能。
三、JVM内存结构
JVM根据存储数据划分:
堆 Heap:作用是存放对象实例和数组。所有线程共享的内存区域,存储程序运行时被创建的所有对象。在虚拟机启动时创建。分为新生代和老年代。GC管理的主要区域。
方法区 Method Area :存储被加载的类信息(元数据)、常量、静态变量、即时编译器编译后的代码等数据。所有线程共享的内存区域。方法区中有运行常量池,用于存放编译期的各种字面值和符号引用。别名叫作“非堆”。
栈 Stack :线程私有,存储局部变量和中间结果。Java方法执行的线程内存模型。每个线程有自己的Java栈区,所有的这些变量都是创建在他们的线程的本地变量,在线程创建时栈区被创建,所有这些本地变量被称为线程本地变量。栈的单位是栈帧,一个方法一个栈帧。
程序计数器 PC register:存储当前正在执行的字节码指令的位置(行号)。在Java中,每个线程有自己独立的程序计数器。
由于JVM的多线程是通过线程轮流切换并分配处理器执行时间来实现,一个处理器只能执行一条线程中的指令。为了线程切换后能恢复到正确的执行位置,每条线程都有一个独立的程序计数器,各个线程之间计数器互不影响,独立存储。也叫线程私有的内存。
本地方法栈:为虚拟机使用到的本地(Native)方法服务。Native 方法是 Java 通过 JNI 直接调用本地 C/C++ 库,如notify,hashcode,wait等都是native方法。
四、JVM垃圾回收机制
四种引用:
强引用:Object obj=new Object(),只要强引用关联还在,垃圾回收器永不回收被引用的对象。
软引用:系统将要发生内存溢出之前,软引用关联的对象会被回收。回收后还是空间不足,才会抛出内存溢出。
例如:一个程序用来处理用户上传的图片。若将所有图片读入内存,这样虽然可以很快的打开图片,但内存空间使用巨大,一些使用较少的图片浪费内存空间,需要手动从内存中移除。如果每次打开图片都从磁盘文件中读取到内存再显示出来,虽然内存占用较少,但一些经常使用的图片每次打开都要访问磁盘,代价巨大。这个时候就可以用软引用构建缓存。
弱引用:一些有用但并非必须,弱引用关联的对象,只能生存大下一次垃圾回收之前,GC发生时,不管内存够不够,都会被回收。
虚引用:幽灵引用,最弱,随时会被回收掉。垃圾回收时收到一个通知,为了监控垃圾回收器是否正常工作。
对象存活判断:
引用计算和根可达分析俩种算法。
引用计算:判断对象的引用数量来决定对象是否可以被回收、每个对象实例都有一个引用计数器,被引用则+1,完成引用则-1。C++和Python采用这种算法,JVM并没有使用。
根可达分析:扫描堆中的对象,沿着GC Root对象为起点的引用链找该对象,找不到则证明此对象不可用。
垃圾回收算法:
当发生GC时,经过GC Roots算法判断为不可达的对象,就会进行对象回收。JVM对象回收主要有复制算法、标记清除、标记整理算法。
复制算法:将可用内存按容量划分为大小相等的两块,每次只使用其中一块。垃圾回收时,将所有存活的对象复制到另一块区域。然后对该区域进行整体清除。
标记-清除算法:标记所有需要回收的对象,标记完成后统一回收。
特点:会存在内存碎片,导致在程序运行中需要分配较大对象时,无法找到足够的连续内存而不得不提前触发GC,执行效率不稳定,需要清理对象越多,效率越低。
标记-整理算法:
标记所有需要回收的对象,标记完成后,将存活对象整理到连续区域。然后将非存活对象清除。
特点:回收后堆空间规整,没有碎片。效率低,涉及到对象移动、引用更新、需要暂停用户线程。
五、Java内存管理
Java内存管理包括内存分配和内存回收两个方面。由JVM自动完成。
new一个Java对象时,即可视为为Java对象申请一个内存空间,JVM会在堆内存中为每个对象分配空间,当一个Java对象失去引用时,JVM的垃圾回收机制就会自动清除他们,并回收它们所占用的内存空间。
当Java对象被创建出来之后,垃圾回收机制GC会实时的监控每一个对象的运行状态。当监控到某个对象不在被引用变量所引用时,垃圾回收机制会立即回收它所占用的空间。
JVM的垃圾回收机制采用有向图方式来管理内存中的对象,可以方便的解决循环引用的问题。
当一个对象在堆内存中运行时,根据它有向图中的状态,可以把状态分为三种:
可达状态:当一个对象被创建后,有一个以上的变量引用它时。有向图中从起始顶点可以到该对象,那它就处于可达状态。程序可通过引用变量来调用该对象的属性和方法。
可恢复状态:若程序中某个对象不再有任何变量引用,它先进入可恢复状态。此时,系统的垃圾回收机制准备回收该对象所占用的内存。在回收之前,系统会调用该对象的finalize方法进行资源清理,如果在调用finalize方法重新让一个以上引用变量引用该对象,则这个对象会再次变为可达状态。否则,该对象将进入不可达状态。
当对象变成不可达时,GC会判断该对象是否覆盖了finalize方法,若未覆盖,则直接将其回收。