A collection of 29 posts

volatile内存模型

Java中的volatile CPU的读写操作 CPU在cache line状态的转化期间是阻塞的,经过长时间的优化,在寄存器和L1缓存之间添加了LoadBuffer、StoreBuffer来降低阻塞时间,LoadBuffer、StoreBuffer,合称排序缓冲(Memoryordering Buffers (MOB)),Load缓冲64长度,store缓冲36长度,Buffer与L1进行数据传输时,CPU无须等待。 CPU执行load读数据时,把读请求放到LoadBuffer,这样就不用等待其它CPU响应, ...

MAC下使用HSDIS生成汇编代码并使用JITWatch查看

jdk 安装jdk8 笔者之前使用的java12版本愣是不能使用HSdis和JITWatch,于是就安装了JDK8。 前往Oracle官网下载jdk8: https://www.oracle.com/java/technologies/javase-jdk8-downloads.html 多版本管理 试了一下使用jenv不能用,还是老实export JAVA_HOME参数。 环境变量配置 /usr/ ...

MAC编译openjdk

配置mac环境:brew install freetype ccache,安装xcode,gcc和make都需要 代码推荐https://hg.openjdk.java.net/jdk/jdk12/ 去获取zip包(点击browse->zip) 下载解压后 chmod ...

JVM字节码调用指令

invokestatic:用于调用静态方法。 invokespecial:用于调用私有实例方法、构造器,以及使用 super 关键字调用父类的实例方法或构造器,和所实现接口的默认方法。 invokevirtual:用于调用非私有实例方法。 invokeinterface:用于调用接口方法。 invokedynamic:用于调用动态方法。 ...

JVM内存 — 《java核心技术》

JVM内存区域划分 程序计数器,每个线程都有自己的程序计数器,存储当前线程正在执行的Java方法的JVM指令地址; Java虚拟机栈,每个线程在创建时都会创建一个虚拟机栈,内部保存一个个栈帧,对应着一次次方法调用。如果在该方法中调用了其他方法,则会创建新的栈帧。栈帧中存储着局部变量表、操作数栈、动态链接、方法正常退出或异常退出的定义等; 堆,放置Java对象实例,是垃圾收集器重点照顾的区域; 方法区,所有线程共享的一块内存区域,用于存储元数据, ...

Java的类加载过程 - 《Java核心技术》

Java类加载步骤 加载:将字节码数据从不同的数据源读取到JVM中,并映射为JVM认可的数据结构; 链接:把原始类定义信息平滑的转化入JVM,包括: 验证:验证字节信息是符合Java虚拟机规范的。 准备:创建类或接口中的静态变量,并初始化静态变量的初始值。 解析:将常量池中的符号引用替换为直接引用。 初始化:真正执行类初始化的代码逻辑,包括静态字段赋值、执行类定义中的静态初始化块内的逻辑。 双亲委派模型 简单来说就是当类加载器试图加载某个类型的时候, ...

LongAdder

LongAdder是jdk8新增的用于高并发环境的计数器。 Atomicxxx使用硬件级别的指令CAS来更新计数器的值,在高并发的情况下每次只能有一个线程能成功,竞争失败的线程会非常多,白白浪费了很多cpu事件,因为竞争失败的线程会自旋。 jdk8的AtomicLong: // jdk1.8的AtomicLong的实现代码,这段代码在sun.misc.Unsafe中 // 当线程竞争很激烈时,while判断条件中的CAS会连续多次返回false,这样就会造成无用的循环,循环中读取volatile变量的开销本来就是比较高的 // 因为这样,在高并发时,AtomicXXX并不是那么理想的计数方式 public ...

Java并发类库提供的线程池有哪几种? 分别有什么特点? - 《Java核心》笔记

通常都是使用Executors提供的通用线程池创建方法去创建不同配置的线程池,主要区别在于不同的ExecutorService类型或者不同的初始参数,主要分为五类: newCachedThreadPool() 用于处理大量短时间工作任务的线程池。会试图缓存线程并重用,当无缓存线程可用时,会创建新的工作线程。如果线程闲置时间超过60秒,则被终止并移出缓存。长时间闲置时,这种线程池不会消耗什么资源。其内部使用SynchronousQueue作为工作队列。 newFixedThreadpool(int nThreads) 重用固定数目的线程,背后使用的是无界的工作队列。如果任务数量超过了nThread,将在工作队列中等待空闲线程出现。 ...

AQS(转)

简介 AQS:AbstractQueuedSynchronizer,队列同步器,Java中同步组件的基础框架。JUC并发包中的核心基础组件。 AQS解决了子类实现同步器时涉及到的大量细节问题,例如获取同步状态、FIFO同步队列。 基于AQS构建的同步器中,只能在一个时刻发生阻塞,从而降低上下文切换的开销,提高了吞吐量。 AQS的主要使用方式是继承,子类通过继承同步器并实现它的抽象方法来管理同步状态。 AQS使用一个int类型的成员变量state来表示同步状态,当state>0时表示已经获取了锁,当state ...

LockSupport源码分析

concurrent包基于AQS框架,AQS框架基于两个类: Unsafe(提供CAS操作) LockSupport(提供park/unpark操作) 概念 LockSupport public static void park() { UNSAFE.park(false, 0L); } public static void ...

并发包中的ConcurrentLinkedQueue和LinkedBlockingQueue有什么区别? - 《Java 核心技术》笔记

Concurrent* 容器,基于lock-free,在常见的多线程访问场景,一般可以提供较高吞吐量; LinkedBlockingQueue内部基于锁,并提供了BlockingQueue的等待性方法。 Concurrent* 性质 java.util.concurrent包提供的容器(Queue、List、Set)、Map从命名上可以大概分为Concurrent* 、CopyOnWrite和Blocking等三类: Concurrent没有类似CopyOnWrite之类容器相对较重的修改开销; Concurrent提供了较低的遍历一致性。比如当利用迭代器遍历时, ...

Java并发包提供的并发工具类 - 《java核心》笔记

提供了各种丰富的同步结构,包括CountDownLatch、CyclicBarrier、Semaphore等,可以实现更加丰富的多线程操作,比如利用Semaphore作为资源控制器,限制同时进行工作的线程数量; 各种线程安全的容器,比如ConcurrentHashMap,有序的ConcurrentSkipListMap,通过类似快照机制实现线程安全的动态数组CopyOnWriteArrayList; 各种并发队列实现,比如BlockedQueue,ArrayBlockingQueue、SynchorousQueue,或针对特定场景的PriorityBlockingQueue等; 强大的Executor,可以创建各种不同类型的线程池,调度任务运行等。 问题 ...

死锁 - 《Java 核心》笔记

定位死锁常见方式 必定会出现死锁的程序: public class DeadLockSample extends Thread { private String first; private String second; public DeadLockSample(String name, String first, String ...

ConcurrentSkipListMap实现原理

跳表介绍 跳表是一种随机层次的数据结构,通过建立多级索引,实现以二分查找遍历一个有序链表。 SkipList让已排序的数据分布在多层链表中,以0~1随机数决定一个数据的向上攀升与否,以时间换空间的一个算法。 SkipList的性质: 多层结构,level通过一定概率随机产生; 每一层都是一个有序链表; 最底层包含所有元素; 如果一个元素出现在第i层,则i层以下也包含这个元素; 每个节点包含两个指针,一个指向同一链表中的下一个元素,一个指向下面一层的元素。 性能对比 ConcurrentHashMap和ConcurrentSkipListMap对比, ...

Phaser

Phaser是JDK1.7引入的一个同步工具类,适用于一些需要分阶段的任务处理。一种人物可以分为多个阶段,现希望保证只有前面一个阶段的任务完成后才能开始后面的任务。这种场景可以使用多个CyclicBarrier来实现,但是需要明确知道总共有多少个阶段,且无法动态修改。Phaser可以同时解决这两个问题。 同步器 作用 CountDownLatch 倒数计时器 CyclicBarrier 循环栅栏,初始时设定参与线程数,当线程到达栅栏后,会等待其他线程的到达。当到达栅栏的总数满足指定数后,所有等待的线程继续执行。 ...

ForkJoinPool

ForkJoinPool的作用 ThreadPoolExecutor每个任务都是单独线程处理的。如果某个任务耗时很大,就可能出现其他线程都在等着这个线程结束的情况。 为了处理这种问题,ForkJoinPool将一个大任务拆分为多个小任务,使用fork可以将小任务分发给其他线程同时处理,使用join可以将多个线程处理的结果进行汇总。 ForkJoinPool的原理 ForkJoinPool中每个线程都有自己的双端列表用于存储任务。这个双端列表对于工作窃取算法非常重要。 public class ForkJoinWorkerThread extends Thread { final ForkJoinPool pool; // 工作线程所在的线程池 ...

协程

why 协程 协程的两大特点: 占用的资源更少; 所有的切换和调度都发生在用户态。 协程是一种轻量级、用户态的执行单元。拥有自己的寄存器上下文和栈。协程调度切换时,把寄存器上下文和栈保存到其他地方。在切回来的时候再恢复回来。 线程:抢占式多任务。 协程:协作式多任务。 不管是进程还是线程,阻塞、切换调度的时候都需要陷入系统调用,由CPU跑调度程序,再由调度程序决定跑哪一个进程( ...

Java Instrument

作用 Java Instrument指的是可以用独立于应用程序之外的代理程序来监测和协助运行在JVM上的应用程序,包括但不限于获取JVM运行时状态,替换和修改类定义。 使用方法 Java SE5及以后的版本,可以在一个普通Java程序运行时,通过-javaagent参数制定一个特定的jar文件(包含Instrumentation代理)来启动Instrumentation的代理程序。 代理类必须提供一个public static void premain(String args, Instrumentation inst) ...

java多线程(5) - Java虚拟机中的锁优化技术以及几种锁的介绍

线程的五种状态: 自旋锁 在程序中,Java虚拟机的开发工程师们在分析过大量数据后发现,共享数据的锁定状态一般只会持续很短的一段时间,为了这段时间去挂起和恢复线程其实并不值得。 如果物理机上有多个处理器,可以让多个线程同时执行的话,就可以让后来的线程“稍微(忙)等一下”,不会放弃处理器事件,看看持有锁的线程会不会很快释放锁。这个“稍微等一下”的过程就是自旋。 锁消除 在动态编译同步块的时候,JIT编译器可以借助一种叫逃逸分析( ...

java多线程(4) - Moniter的实现原理

Moniter是什么 我们可以把监视器理解为包含一个特殊的房间的建筑物,这个特殊房间同一时刻只能有一个客人。如果一个顾客想要进去这个房间,就需要在走廊(Entry Set)排队,调度器将基于某个标准来选择排队的客户进入房间。如果用户暂时因为其他事情无法脱身,那么就会被送到等待室(Wait Set)。 监视器是一个用来监视这些线程进入特殊的房间的,它的义务是保证(同一时刻)只有一个线程可以访问被保护的数据和代码。 Monitor其实是一种同步机制,通常被描述为一个对象,其主要特点是: ...

java多线程(2) - Java的对象模型

java对象保存在堆内存中。在内存中,一个Java对象包含三部分:对象头、实例数据和对象填充。 对象头中包含锁状态标志、线程持有的锁等标志。 Java的对象模型 在JVM的内存结构中,对象保存在堆内存中。我们在对对象进行操作时,操作的是对象的引用。 那么对象本身在JVM中的结构是什么样的? oop-klass model HotSpot JVM的设计者不想让每个对象中都含有一个vtable(虚函数表)(HotSpot基于C++实现) ...

谈谈接口和抽象类有什么区别? - 《java核心技术》笔记

回答 接口是行为的抽象,是抽象方法的集合,目的是API定义和实现分离。不包含任何非常量成员以及非静态方法;java8后增加了default method,java9后可以定义private defult method。比如java8中的Collection增加了一系列关于Lambda、Stream的default method。 抽象类是不能被实例化的类,主要目的是代码重用。 S.O.L.I.D原则 ...

Java有几种文件拷贝方式?哪一种最高效? - 《java核心技术》笔记

java的几种文件拷贝方式?哪一种最高效? java.io的FileInputStream/FileOutputStream或者java.nio的transferTo/transferFrom。 nio方式可能更快,因为使用了零拷贝技术,数据传输不需要切换到用户态参与,减少了上下文切换和不必要的内存拷贝。 比如应用读取数据时,先在内核态将数据从磁盘读取到内核缓存,再切换到用户态将数据从内核缓存读取到用户缓存。 而NIO transferTo则直接在内核中进行数据拷贝。 提高类似拷贝等IO操作的性能的原则 使用缓存,减少IO次数; 使用transferTo等机制, ...

Java提供了哪些IO方式? NIO如何实现多路复用? - 《java核心技术》笔记

简述 同步阻塞IO库:传统的java.io包下面一些熟知的IO功能,比如File抽象,输入输出流等,交互方式是同步阻塞。java.net下面提供的部分网络API,比如Socket、ServerSocket、HttpURLConnection; 1.4引入的NIO,提供了Channel、Selector、Buffer等新的抽象,可以用于构建多路复用的、同步非阻塞IO,同时提供了更接近OS底层的高性能数据操作方式; ...

如何保证集合是线程安全的? ConcurrentHashMap如何实现高效地线程安全? - 《java核心技术》笔记

简述 问:为什么需要ConcurrentHashMap? 答:因为Hashtable本身比较低效,它在所有的put、get、size方法上面加上了“synchronized”。而Collections提供的同步包装器,只是将“this”作为互斥的mutex。 早期的ConcurrentHashMap基于: 分段锁,将内部进行分段(Segment),里面是HashEntry的数组; HashEntry内部使用volatile的value字段保证可见性,也利用了不可变对象的机制以改进利用Unsafe提供的底层能力, ...

Exception和Error的区别-《Java核心技术》笔记

Exception是程序正常运行时可以预料到并且应该被捕获的情况,Error是指在正常情况下不太可能出现的情况。绝大部分Error都会导致程序处于非正常、不可恢复的情况,所以不便于也不应该被捕获,比如OutofMemoryError。Exception分为checked和unchecked,checked在代码里必须显式捕获,比如IOException,而unchecked是不可查异常,比如NullPointerException、ArrayIndexOutOfBoundException,通常是可以编码避免的逻辑错误。 抛异常的最佳实践:throw early,catch late。 try-catch会产生额外的性能开销,建议仅捕获有必要的代码段, ...

“解释执行”的理解-《Java核心技术》笔记

编译是将源程序翻译成可执行的目标代码,翻译与执行是分开的。而解释是对源程序的翻译与执行一步到位完成,不生成可存储的目标代码。最大的区别在于控制权:对于解释执行而言,程序运行时的控制权在解释器而不在用户程序;对编译执行而言,程序运行时的控制权在用户。 根据程序执行时的基本表示是实际计算机上的机器语言还是虚拟机的机器语言,可以分为两大类:编译型语言和解释型语言。 由编译型语言编写的源程序需要经过编译、汇编和链接才能生成目标代码,然后机器执行目标代码得出结果。目标代码由机器指令组成,一般不能独立运行; 解释型语言中,翻译器并不生成目标代码,而是产生易于执行的中间代码, ...

java一些优化点

减少临时对象; 减少list的拷贝,如果要添加到末尾,不要新增一个临时对象; 在初始化map和list的时候,在一开始设置初始化容量,减少扩容引起的数据拷贝; if else如果是string,用switch替换; 删除缓存可能会引起性能提升; 可以按照index寻址的,不要用map。 其实思路跟C++差不多。 ...