垃圾回收算法
垃圾收集的三个经典问题:
1.哪些内存需要回收?(对象是否可以被回收的两种经典算法: 引用计数法和可达性分析算法)
2.什么时候回收?(堆的新生代、老年代、永久代的垃圾回收时机,MinorGC和FullGC)
3.如何回收?(三种经典垃圾回收算法(标记清除算法、复制算法、标记整理算法)及分代收集算法和七种垃圾收集器))
本篇文章主要针对上述第三个问题详细介绍下垃圾回收中的相关算法。
标记阶段:引用计数算法标记阶段的目的垃圾标记阶段:主要是为了判断对象是否存活
在堆里存放着几乎所有的 Java 对象实例,在 GC 执行垃圾回收之前,首先需要区分出内存中哪些是存活对象,哪些是已经死亡的对象。只有被标记为己经死亡的对象,GC 才会在执行垃圾回收时,释放掉其所占用的内存空间,因此这个过程我们可以称为垃圾标记阶段。
那么在 JVM 中究竟是如何标记一个死亡对象呢?简单来说,当一个对象已经不再被任何的存活对象继续引用时,就可以宣判为已经死亡。
判断对象存活一般有两种方式:引用计数算法和可达性分析算法。
引用计数算法
引用计数算法(Reference Counting)比较简单,对每个对象保 ...
垃圾回收概述
垃圾回收简介什么是垃圾?
垃圾是指在运行程序中没有任何指针指向的对象,这个对象就是需要被回收的垃圾。
如果不及时对内存中的垃圾进行清理,那么,这些垃圾对象所占的内存空间会一直保留到应用程序结束,被保留的空间无法被其他对象使用,甚至可能导致内存溢出。
什么是垃圾回收 ?
垃圾回收(Garbage Collection,GC),顾名思义就是释放垃圾占用的空间,防止内存泄露。有效的使用可以使用的内存,对内存堆中已经死亡的或者长时间没有使用的对象进行清除和回收。
Java 的垃圾收集 Garbage Collection 通常被称为 “GC” ,它诞生于1960年 MIT 的 Lisp 语言,经过半个多世纪,目前已经十分成熟了。
垃圾收集有三个最经典的问题:
1.哪些内存需要回收?(对象是否可以被回收的两种经典算法: 引用计数法和可达性分析算法)
2.什么时候回收?(堆的新生代、老年代、永久代的垃圾回收时机,MinorGC和FullGC)
3.如何回收?(三种经典垃圾回收算法(标记清除算法、复制算法、标记整理算法)及分代收集算法和七种垃圾收集器))
为什么需要 GC ?
对于 ...
字符串常量池(StringTable)
String 的基本特性
String:字符串,使用一对 “ “ 引起来表示。
12String s1 = "hello" ; // 字面量的定义方式String s2 = new String("hello"); // new 对象的方式
String 被声明为 final 的,不可被继承。
String 实现了 Serializable 接口:表示字符串是支持序列化的;实现了 Comparable 接口:表示 String 可以比较大小。
String 在 jdk8 及以前内部定义了 final char value[] 用于存储字符串数据,jdk9 时改为 byte[] 。
为什么 JDK9 改变了 String 的结构?
官方文档:http://openjdk.java.net/jeps/254
为什么改为 byte [] 存储?
String 类的当前实现将字符存储在 char 数组中,每个字符使用两个字节(16位)。从许多不同的应用程序收集的数据表明,字符串是堆的主要组成部分,而且大多数字符串对象只包含拉 ...
执行引擎
执行引擎概述
执行引擎是 Java 虚拟机核心的组成部分之一。
“虚拟机”是一个相对于“物理机”的概念,这两种机器都有代码执行能力,其区别是物理机的执行引擎是直接建立在处理器、缓存、指令集和操作系统层面上的,而虚拟机的执行引擎则是由软件自行实现的,因此可以不受物理条件制约地定制指令集与执行引擎的结构体系,能够执行那些不被硬件直接支持的指令集格式。
JVM 的主要任务是负责装载字节码到其内部,但字节码并不能够直接运行在操作系统之上,因为字节码指令并非等价于本地机器指令,它内部包含的仅仅只是一些能够被 JVM 所识别的字节码指令、符号表,以及其他辅助信息。
如果想要让一个 Java 程序运行起来,执行引擎(Execution Engine)的任务就是将字节码指令解释/编译为对应平台上的本地机器指令才可以。简单来说, JVM 中的执行引擎充当了将高级语言翻译为机器语言的译者。
前端编译:从 Java 程序—字节码文件的这个过程叫前端编译。
执行引擎这里有两种行为:一种是解释执行,一种是编译执行(这里的是后端编译)。
执行引擎工作过程
执行引擎在执行的过程中究竟需要执行什么样的字节码指令 ...
直接内存
直接内存直接内存概述
不是虚拟机运行时数据区的一部分,也不是《Java虚拟机规范》中定义的内存区域。
直接内存是在 Java 堆外的、直接向系统申请的内存区间。
来源于 NIO,通过存在堆中的 DirectByteBuffer 操作 Native 内存。
通常,访问直接内存的速度会优于 Java 堆。即读写性能高。
因此出于性能考虑,读写频繁的场合可能会考虑使用直接内存。
Java 的 NIO 库允许 Java 程序使用直接内存,用于数据缓冲区 。
12345678910111213141516171819202122232425/** * IO NIO (New IO / Non-Blocking IO) * byte[] / char[] Buffer * Stream Channel * * 查看直接内存的占用与释放 */public class BufferTest { private static final int BUFFER = 1024 * 1024 * 1024 ...
对象的内存布局与访问定位
对象的创建面试题
对象在JVM中是怎么存储的?
对象头信息里面有哪些东西?
Java对象头有什么?
从对象创建的方式和步骤开始:
对象创建的方式
new:最常见的方式、单例类中调用 getInstance 的静态类方法,XXXFactory 的静态方法;
Class 的 newInstance 方法:在 JDK9 里面被标记为过时的方法,因为只能调用空参构造器;
Constructor 的 newInstance(XXX) :反射的方式,可以调用空参的,或者带参的构造器;
使用 clone():不调用任何的构造器,要求当前的类需要实现 Cloneable 接口中的 clone 接口;
使用序列化:序列化一般用于 Socket 的网络传输;
第三方库 Objenesis。
对象创建的步骤从字节码看待对象的创建过程:
示例代码:
12345public class ObjectTest { public static void main(String[] args) { Object obj = new Object(); } ...
本地方法接口与本地方法栈
本地方法接口
- 简单地讲,一个 Native Method 是一个 Java 调用非 Java 代码的接囗。一个 Native Method 是这样一个 Java 方法:该方法的实现由非 Java 语言实现,比如 C 。这个特征并非 Java 所特有,很多其它的编程语言都有这一机制,比如在 C++ 中,你可以用 extern "c" 告知 c++ 编译器去调用一个 c 的函数。
- "A native method is a Java method whose implementation is provided by non-java code."(本地方法是一个非Java的方法,它的具体实现是非Java代码的实现)。
- 在定义一个 native method 时,并不提供实现体(有些像定义一个 Java interface ),因为其实现体是由非 java 语言在外面实现的。
- 本地接口的作用是融合不同的编程语言为 Java 所用,它的初衷是融合 C/C++ 程序。
代码举例说明 Native 方法是如何编写的:
123456public class IhaveN ...
方法区
方法区概述前言方法区是运行时数据区的最后一个部分。从线程共享与否的角度来看:
栈、堆、方法区的交互关系
Person 类的 .class 信息存放在方法区中;
person 变量存放在 Java 栈的局部变量表中;
真正的 person 对象存放在 Java 堆中;
在 person 对象中,有个指针指向方法区中的 person 类型数据,表明这个 person 对象是用方法区中的 Person 类 new 出来的。
方法区的理解
官方文档:https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-2.html#jvms-2.5.4
方法区在哪里:
《Java虚拟机规范》中明确说明:尽管所有的方法区在逻辑上是属于堆的一部分,但一些简单的实现可能不会选择去进行垃圾收集或者进行压缩。但对于 HotSpot JVM 而言,方法区还有一个别名叫做Non-Heap(非堆),目的就是要和堆分开。
所以,方法区可以看作是一块独立于Java堆的内存空间 。
方法区的基本理解:**方法区主要存放的是 Class,而堆中主要存放的是 ...
堆
堆的基本概述堆的概念《Java虚拟机规范》中对Java堆的描述是:所有的对象实例以及数组都应当在运行时分配在堆上。(The heap is the run-time data area from which memory for all class instances and arrays is allocated)。
但从实际使用角度看,“几乎”所有的对象实例都在这里分配内存。因为还有一些对象是在栈上分配的,而数组和对象可能永远不会存储在栈上,因为栈帧中保存引用,这个引用指向对象或者数组在堆中的位置。
堆针对一个JVM进程来说是唯一的,也就是说一个进程对应着一个JVM实例,但是进程包含多个线程,他们是共享同一堆空间的;
一个JVM实例只存在一个堆内存,堆是Java内存管理的核心区域;
Java堆区在JVM启动的时候即被创建,其空间大小也就确定了,是JVM管理的最大一块内存空间;
但堆内存的大小是可以调节的;
《Java虚拟机规范》规定,堆可以处于物理上不连续的内存空间中,但在逻辑上它应该被视为连续的(可类比操作系统);
Java堆中可以划分线程私有的缓冲区(Thread Local ...
虚拟机栈
虚拟机栈概述由于跨平台性的设计,Java的指令都是根据栈来设计的。由于不同平台CPU架构不同,所以不能设计为基于寄存器的。 其优点是跨平台,指令集小,编译器容易实现,缺点是性能下降,实现同样的功能需要更多的指令。
为什么不少Java开发人员一提到Java内存结构,就会非常粗粒度地将JVM中的内存区理解为仅有Java堆(heap)和Java战(stack)?
首先栈是运行时的单位,而堆是存储的单位:
栈解决程序的运行问题,即程序如何执行,或者说如何处理数据。
堆解决的是数据存储的问题,即数据怎么放,放哪里。
Java虚拟机栈是什么
Java虚拟机栈(Java Virtual Machine Stack),早期也叫Java栈。每个线程在创建时都会创建一个虚拟机栈,其内部保存一个个的栈帧(StackFrame),对应着一次次的Java方法调用。
Java虚拟机栈是线程私有的。
生命周期生命周期和线程一致,即线程结束了,该虚拟机栈也销毁了。
作用主管Java程序的运行,它保存方法的局部变量、部分结果,并参与方法的调用和返回。
局部变量,它是相比于成员变量来说的(或属性 ...