Jaqen's Blog

Jaqen's Blog

Java 内存分配和回收策略
Java 的内存分配主要是在程序运行时给对象在堆上分配内存。通常将堆内存结构按新生代和老年代进行划分,堆内存结构图如下: 新生代大部分对象创建和销毁的区域。 内部包含 Eden 区域,作为对象初始分配的区域;两个 Survivor,也叫 from、to 区域,用来放置从 Minor GC 中生存下来的对象。 TLAB 对 Eden 区域再进行划分, Hotspot JVM 还有一个概念叫着 Thread Local Allocation(TLAB),这是 JVM 为每个线程分配的一个私有缓存区域。多线程同时分配内存时,为了避免操作同一地址,可能需要使用加锁机制,进而影响分配速度。TLA...
JVM 安全点介绍
什么是安全点?在 JVM 中如何判断对象可以被回收 一文中,我们知道 HotSpot 虚拟机采取的是可达性分析算法。即通过 GC Roots 枚举判定待回收的对象。 那么,首先要找到哪些是 GC Roots。 有两种查找 GC Roots 的方法: 一种是遍历方法区和栈区查找(保守式 GC)。 一种是通过 OopMap 数据结构来记录 GC Roots 的位置(准确式 GC)。 很明显,保守式 GC 的成本太高。准确式 GC 的优点就是能够让虚拟机快速定位到 GC Roots。 对应 OopMap 的位置即可作为一个安全点(Safe Point)。 在执行 GC 操作时,所有的工作线程必...
JVM 七种垃圾收集器
Java 垃圾收集器是 垃圾收集算法 的具体实现。 下图展示的是 7 种作用于不同分代的收集器,如果两种收集器之前有连接,表示它们可以配合使用。收集器所在的位置表示它是属于新生代收集器还是老年代收集器。 Serial 收集器单线程、串行收集器。即在垃圾清理时,必须暂停其他所有工作线程。 它是采用复制算法的新生代收集器。 下图是 Serial 收集器的运行过程。 ParNew 收集器ParNew 收集器是 Serial 收集器的多线程版本。除了使用多线程收集,其他与 Serial 收集相比并无太多创新之处。 默认开启的线程数量与 CPU 数量相同。 在单 CPU 的环境,ParNew ...
Java 垃圾收集算法有哪些?
本文主要介绍几种 Java 垃圾收集算法的原理及其优缺点。 标记清除(Mark-Sweep)算法首先进行标记工作,标识出所有要回收的对象,然后进行统一回收被标记的对象。 对象标记的过程在 Java 对象的自我救赎 一文中有介绍。执行过程如下图: 它的不足之处在于: 1、标记、清除的效率都不高。 2、清除后产生大量的内存碎片,空间碎片太多会导致在分配大对象时无法找到足够大的连续内存,从而不得不触发另一次垃圾回收动作。 复制(Copying)算法将可用内存按容量分成大小相等的两块,每次只使用其中的一块。 当这一块内存用完了,就将还存活的对象复制到另外一块上面,再把已使用过的内存空间一次清理...
Java 对象的自我救赎
JVM 通过可达性分析算法判断一个对象是否可以被回收 ,但并不是一个对象不可达时,就宣告“死刑”的,此时只是暂时处于”缓刑“阶段。要宣告一个对象“死刑”,至少还要经历两次标记过程。 没有必要执行 finalize() 方法的筛选条件取决于: 1、 finalize() 方法已经被执行过(finalize()`只会执行一次)。 2、对象没有重写 finalize()方法。 如果一个对象有必要执行 finalize() 方法,会进入 F-Queue 队列,等待 Finalizer 线程执行。 因此如果想要完成对象自救, finalize()是逃脱死亡的最后一次机会,重新与引用链上的任何...
JVM 中如何判断对象可以被回收?
JVM 的垃圾回收器主要关注的是堆上创建的实例对象,在每次对这些对象进行回收前,需要确定哪些对象是可以去进行回收的。 主要有下面两种方法。 引用计数算法给对象添加一个引用计数器,当有一个地方引用它,计数器值加 1;当引用失效时,计数器值减 1。任何时刻计数器值为 0 表示这个对象可以被回收了。 优点: 判断效率高,实现简单。 不足之处: 难以解决对象之间相互循环引用的问题。 比如: 12345678910111213141516171819public class GCDemo { public static void main(String[] args) &...
JVM 中的内存溢出
内存溢出,通俗一点,就是 JVM 内存不足了,没有空闲内存,并且垃圾收集器也无法提供更多内存。 这里的意思是说,通常在抛出 OutOfMemoryError 之前,垃圾收集器会被触发,尽其所能去清理空间。 但也不是在所有情况下垃圾回收器都会被触发,比如分配了一个大对象,超过了堆的最大值,JVM 可能判断出垃圾收集并不能解决这个问题,直接抛出 OutOfMemoryError 。 在 JVM内存结构 中,除了程序计数器,其他区域都有可能发生 OutOfMemoryError 。 堆溢出通过-Xms 和Xmx分别设定堆最小值和最大值。 错误信息: 1java.lang.OutOfMemory...
JVM内存结构
Java 虚拟机在执行 Java 程序的过程中会把它管理的内存划分为若干个不同的数据区域。 这些区域中,一些是线程私有的,一些是线程共享的。 线程私有的:程序计数器、虚拟机栈、本地方法栈 线程共享的:堆、方法区、直接内存 程序计数器一块较小的内存空间,用于标记当前线程所执行字节码的行号。 Java 虚拟机的多线程是通过线程轮流切换并分配处理器执行时间的方式实现,所以确定的时刻一个处理器只会执行一个线程中的指令。 为了线程切换后能恢复到正确的执行位置,每个线程都需要一个独立的程序计数器,用于记录线程所执行字节码指令的地址。 虚拟机栈Java 虚拟机栈是由一个个帧栈组成。 每个方法执行时会...
为什么 byte 的范围是 -128~127?
这是一个很基础的问题。但是在昨天之前,我都是只知其然,不知其所以然。 于是我搜索了大量网络资料。说实话,看完大部分文章,我还是没有弄明白为什么。直到我看到了知乎上面的一个回答: 在8位二进制中,-128 没有原码、反码形式,那么它的补码是怎么计算出来的?还是约定的? ,醍醐灌顶。 这里尝试自己阐述一遍,如果你没有看懂,那是我的问题,还是直接看参考链接。 首先,忘记原码、补码、反码的概念。 从 「模」开始。什么是模? 想象日常见到的时钟,它可以表示 0 - 12 点的时间。假设当前时针指向 8 点,而准确时间是 5 点。那么调整方法有两种: 一种方法是将逆时针拨 3 小时,8 - 3 = ...
2018 年过去了,我很怀念它
每到年底,大家都会总结过去的一年里的得与失,收获与不足,也会为新的一年立下新的 Flag。 有时候在想为什么要有新年的感念,也许就是希望吧。每个人都需要一个时间周期去不断给自己寄予新的希望,而这个周期不能太长,以便可以安慰自己的平庸,这个周期不能太短,至少可以笃定的注视前方。 回望这一年,也是有几件值得纪念的事情。 1、3 月 8 日,老鲍回国,大学兄弟们聚得最齐的一次。 2、5 月 20 日和女友去第一次去看了朴树的演唱会。 3、6 月 19 日和女朋友领了证。 4、11 月 09 日进新房,双方父母第一次见面。 5、12 月 15 日,看了许巍的演出,第一次与许巍合影。 其实每一...
avatar
Jaqen Ng