jvm原理

Java Thread系列(五)synchronized

南笙酒味 提交于 2020-03-01 03:14:28
Java Thread系列(五)synchronized 本文我们讨论 synchronized 重量级锁的实现原理。 一、synchronized 实现原理 1.1 synchronized 修饰符对应的字节码指令 我们知道在 java中synchronized 主要有两种使用形式:同步方法和同步代码块。 synchronized 可以保证方法或者代码块在运行时,同一时刻只有一个方法可以进入到临界区,同时它还可以保证共享变量的内存可见性。Java 中每一个对象都可以作为锁,这是 synchronized 实现同步的基础: (1)普通同步方法,锁是当前实例对象 (2)静态同步方法,锁是当前类的class对象 (3)同步方法块,锁是括号里面的对象接下来 public class SynchronizedTest { // 作用于类级别 public synchronized static void testClass() { System.out.println("synchronized testClass!!!"); } // 作用于方法级别 public synchronized void testMethod() { System.out.println("synchronized testMethod!!!"); } // 作用于代码块级别 public void

简述JVM的内存布局

老子叫甜甜 提交于 2020-02-28 13:40:53
JVM中将内存分为若干部分:堆、方法区、虚拟机栈、本地方法栈、程序计数器。 程序计数器:该区域是内存中较小的一块区域,是当前线程在执行的字节码的行号指示器。程序计数器是线程私有的,每个线程都有一个程序计数器,线程之间的程序计数器相互独立,互不干扰。是Java虚拟机规范中唯一一个没有规定任何 OutOFMemory Error 错误的区域。 虚拟机栈:是线程私有的,其生命周期与线程相同。虚拟机栈描述的是Java方法执行的内存模型。每个方法在执行时都会创建一个栈帧用于存储局部变量表,操作数栈、动态链接、方法出口等信息。每个方法从调用到结束都会有栈帧在虚拟机栈中入栈或出栈。一个方法的调用链可能会很长,于是当调用一个方法时,可能会有很多的方法都处于执行状态,但是对于执行引擎来讲,位于虚拟机栈顶的栈帧才是有效的,这个栈帧被称为当前栈帧,这个栈帧所关联的方法称为当前方法,执行引擎的所有指令都是针对当前战争进行操作的。StackOverFlowError异常(当线程申请的栈空间大于虚拟机所允许的深度时),OutOfMemoryError异常:当虚拟机栈无法申请到足够内存时。局部变量表示一组变量值的存储空间,局部变量表的存储单位是slot。若是实例方法,则第0个slot是存储的指向所有实例对象的引用。对于操作数栈,在方法刚开始执行时操作数栈为空,执行过程中会有各种字节码指令写入或者弹出

jvm 垃圾回收

主宰稳场 提交于 2020-02-27 17:40:49
jvm 垃圾回收 引用类型 强引用:发生 gc 的时候不会被回收 软引用:有用但不是必须的对象,在发生内存溢出之前会被回收 弱引用:有用但不是必须的对象,在下一次 GC 时会被回收 虚引用(幽灵引用/幻影引用):无法通过虚引用获得对象 用 PhantomReference 实现虚引用,虚引用的用途是在 gc 时返回一个通知 垃圾辨别方法 引用计数器 为每个对象创建一个引用计数,有对象引用时计数器 +1,引用被释放时计数 -1 当计数器为 0 时就可以被回收。缺点是不能解决循环引用的问题 可达性分析 从 GC Roots 开始向下搜索,搜索所走过的路径称为引用链 当一个对象到 GC Roots 没有任何引用链相连时,则证明此对象是可以被回收的 GC Roots,GC 的根集合, 是一组必须活跃的引用 可作为 GC Roots 的对象有: 虚拟机(栈帧中的本地变量表)中引用的对象 方法区中类静态属性引用的对象 方法区中常量引用的对象 本地方法栈中 JNI(即一般说的 native 方法)中引用的对象 垃圾收集算法 引用计数(Reference Counting) 原理是此对象有一个引用,即增加一个计数,删除一个引用则减少一个计数 垃圾回收时,只用收集计数为 0 的对象 缺点:无法处理循环引用问题 标记-清除(Mark-Sweep) 第一阶段从引用根节点开始标记所有被引用的对象

JVM如何判断对象能否被回收

十年热恋 提交于 2020-02-24 10:53:45
•写在前面 说起Java和C++,很容易想到让人疯狂的指针,Java使用了内存动态分配和垃圾回收技术,让我们从C++的各种指针问题中摆脱出来,更加专心于业务逻辑,不过如果我们需要深入了解java的JVM相关原理,我们必须要面对这些东西,深入了解JVM在内存动态分配和垃圾回收技术的原理知识,这篇文章就是来做一个先导,在jvm进行垃圾回收之前,它必须要知道回收的对象是否已“死”,这样才能保证程序的正常稳定。 •对象的创建 我们将回收对象前,先讲讲在虚拟机上,对象是怎么被创建的。在我们编写代码的角度(语言层面)来看,我们创建一个对象实例,只需要使用new关键词就完事儿了,很简单,不过你享受的简单是因为虚拟机帮你承受了所有繁琐的工作,那虚拟机是怎么工作创建一个对象的呢? 当虚拟机遇到new指令时,首先会去检查这个指令的参数是否能在常量池中定位到一个类的符号引用(没有类,创建个锤子的对象),并且检查这个符号引用代表的类是否已被加载、解析和初始化过,如果没有,那必须要执行相应的类加载过程。这是第一步,在类加载检查过后,接下来虚拟机将为新生对象分配内存,对象所需的内存大小在类加载完成后便已经完全确定了(这里插一句,如何确定的?这就和对象的内存布局有关了,对象在内存中的布局可以分为3个区域,分别是对象头、实例数据和对齐填充,对象头里面存的是对象自身的运行时数据,比如哈希码、GC分代年龄、锁状态

分布式锁原理

微笑、不失礼 提交于 2020-02-23 10:54:29
分布式锁原理 单体服务中,使用jvm内的Synchronized和Lock即可,分布式环境下,多台机器,对应多个jvm,多个进程,因此需要使用统一的分布式锁进行同步操作 ZK实现分布式锁的核心是:临时有序节点 临时可以防止死锁,有序可以保证同步(InterProcessMutex) 来源: CSDN 作者: 414丶小哥 链接: https://blog.csdn.net/u010838785/article/details/104455352

java基础知识——反射

…衆ロ難τιáo~ 提交于 2020-02-15 02:14:16
反射原理 除了int,float等基本类型外,Java的其他类型全部都是class(包括interface),即class的本质是数据类型(Type) 。而class是由JVM在执行过程中动态加载的。JVM在第一次读取到一种class类型时(也是第一次要使用时才读取),将其加载进内存。每加载一种class,JVM就为其创建一个Class类型的实例(例如:Object 类中的 getClass( ) 方法就将返回一个 Class 类型的实例),并关联起来。注意: 这里的Class类型是一个名叫Class的class 。源码如下: public final class Class { private Class() {} } 以String类为例,当JVM加载String类时,它首先读取String.class文件到内存,然后,为String类创建一个Class实例并与之关联起来: Class cls = new Class(String); 这个Class实例是JVM内部创建的,我们自己的Java程序是无法创建Class实例的,因为Class类的构造方法是private,所以, JVM持有的每个Class实例都指向一个数据类型(类或interface) : 并且,一个Class实例包含了该class的所有完整信息: 由于JVM为每个加载的class创建了对应的Class实例

第 6 章 Spark 内存管理

北城余情 提交于 2020-02-12 21:17:41
上篇: 第 5 章 Spark Shuffle解析 Spark 内存管理 在执行Spark 的应用程序时,Spark 集群会启动 Driver 和 Executor 两种 JVM 进程,前者为主控进程,负责创建 Spark 上下文,提交 Spark 作业(Job),并将作业转化为计算任务(Task),在各个 Executor 进程间协调任务的调度,后者负责在工作节点上执行具体的计算任务,并将结果返回给 Driver,同时为需要持久化的 RDD 提供存储功能。由于 Driver 的内存管理相对来说较为简单,本节主要对 Executor 的内存管理进行分析,下文中的 Spark 内存均特指 Executor 的内存。 1、堆内和堆外内存规划 作为一个 JVM 进程, Executor 的内存管理建立在 JVM 的内存管理之上,Spark 对 JVM 的堆内(On-heap)空间进行了更为详细的分配,以充分利用内存。同时,Spark 引入了堆外(Off-heap)内存,使之可以直接在工作节点的系统内存中开辟空间,进一步优化了内存的使用 。 堆内内存受到JVM统一管理,堆外内存是直接向操作系统进行内存的申请和释放。 堆内内存 堆内内存的大小,由 Spark 应用程序启动时的 –executor-memory 或 spark.executor.memory 参数配置。Executor

Java多线程(二)

最后都变了- 提交于 2020-02-12 11:33:30
个人博客 http://www.milovetingting.cn Java多线程(二) 前言 本文为学习Java相关知识所作笔记,参考以下资料: https://github.com/Snailclimb/JavaGuide ,感谢原作者的分享! JAVA 锁 乐观锁 乐观锁是一种乐观思想,即认为读多写少,遇到并发写的可能性低,每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在更新的时候会判断一下在此期间别人有没有去更新这个数据,采取在写时先读出当前版本号,然后加锁操作(比较跟上一次的版本号,如果一样则更新),如果失败则要重复读-比较-写的操作。 java 中的乐观锁基本都是通过 CAS 操作实现的,CAS 是一种更新的原子操作,比较当前值跟传入值是否一样,一样则更新,否则失败。 悲观锁 悲观锁是就是悲观思想,即认为写多,遇到并发写的可能性高,每次去拿数据的时候都认为别人会修改,所以每次在读写数据的时候都会上锁,这样别人想读写这个数据就会 block 直到拿到锁。java中的悲观锁就是Synchronized,AQS框架下的锁则是先尝试cas乐观锁去获取锁,获取不到,才会转换为悲观锁,如 RetreenLock。 自旋锁 自旋锁原理非常简单,如果持有锁的线程能在很短时间内释放锁资源,那么那些等待竞争锁的线程就不需要做内核态和用户态之间的切换进入阻塞挂起状态

深入理解JVM之垃圾收集算法

 ̄綄美尐妖づ 提交于 2020-02-09 05:18:07
垃圾收集算法 1标记-清除算法(Mark-Sweep)   实现:先标记后清除   算法分为“标记”和“清除”两个阶段:首先标记出所有需要回收的对象,在标记完成后统一回收所有被标记的对象。   主要缺点:内存碎片   它的主要不足空间问题,标记清除之后会产生大量不连续的内存碎片,空间碎片太多可能会导致以后在程序运行过程中需要分配较大对象时,无法找到足够的连续内存而不得不提前触发另一次垃圾收集动作。 2复制算法(Copying) 一般用于新生代GC 优点:   避免内存碎片的问题。新生代GC使用此种算法,分为Eden:Survivor1:Survivor2=8:1:1 缺点: 1、存活对象较多时,复制效率低   2、不想浪费50%空间的话,需要有额外的空间担保 实现:   将可用内存按容量划分为大小相等的两块,每次只使用其中的一块。当这一块的内存用完了,就将还存活着的对象复制到另外一块上面,然后再把已使用过的内存空间一次清理掉。这样使得每次都是对整个半区进行内存回收,内存分配时也就不用考虑内存碎片等复杂情况,只要按顺序分配内存即可,实现简单,运行高效。只是这种算法的代价是将内存缩小为了原来的一半。 3标记-整理算法(Mark-Compact)   一般用于老年代GC   标记过程与“标记-清除”一样,首先标记出所有需要回收的对象,在标记完成后,后续步骤不是直接对可回收对象进行清理

深入理解JDK中的Reference原理和源码实现

故事扮演 提交于 2020-02-07 00:39:24
前提 这篇文章主要基于JDK11的源码和最近翻看的《深入理解Java虚拟机-2nd》一书的部分内容,对JDK11中的 Reference (引用)做一些总结。值得注意的是,通过笔者对比一下JDK11和JDK8对于 java.lang.ref 包的相关实现,发现代码变化比较大, 因此本文的源码分析可能并不适合于JDK11之外的JDK版本 。 Reference的简介和分类 在JDK1.2之前,Java中的引用的定义是十分传统的:如果reference类型的数据中存储的数值代表的是另一块内存的起始地址,就称这块内存代表着一个引用。在这种定义之下,一个对象只有被引用和没有被引用两种状态。 实际上,我们更希望存在这样的一类对象:当内存空间还足够的时候,这些对象能够保留在内存空间中;如果当内存空间在进行了垃圾收集之后还是非常紧张,则可以抛弃这些对象。基于这种特性,可以满足很多系统的缓存功能的使用场景。 java.lang.ref 包是JDK1.2引入的,包结构和类分布如下: - java.lang.ref - Cleaner.class - Finalizer.class - FinalizerHistogram.class - FinalReference.class - PhantomReference.class - Reference.class - ReferenceQueue