Parallel Scavenge

Parallel Scavenge Parallel Scavenge收集器的特点是它的关注点与其他收集器不同,CMS等收集器的关注点尽可能地缩短垃圾收集时用户线程的停顿时间,而Parallel Scavenge收集器的目标则是达到一个可控制的吞吐量 (Throughput) 。所谓吞吐量就是CPU用于运行用户代码的时间与CPU总消耗时间的比值,即吞吐量 = 运行用户代码时间 / (运行用户代码时间 + 垃圾收集时间) ,虚拟机总共运行了100分钟,其中垃圾收集花掉1分钟,那吞吐量就是99%。 停顿时间越短就越适合需要与用户交互的程序,良好的响应速度能提升用户的体验;而高吞吐量则可以最高效率地利用CPU时间,尽快地完成程序的运算任务,主要适合在后台运算而不需要太多交互的任务。 Parallel Scavenge收集器提供了两个参数用于精确控制吞吐量,分别是控制最大垃圾收集停顿时间的-XX:MaxGCPauseMillis参数及直接设置吞吐量大小的 -XX:GCTimeRatio参数。 MaxGCPauseMillis参数允许的值是一个大于0的毫秒数,收集器将尽力保证内存回收花费的时间不超过设定值。不过大家不要异想天开地认为如果把这个参数的值设置得稍小一点就能使得系统的垃圾收集速度变得更快,GC停顿时间缩短是以牺牲吞吐量和新生代空间来换取的: 系统把新生代调小一些,收集300MB新生代肯定比收集500MB快吧,这也直接导致垃圾收集发生得更频繁一些,原来10秒收集一次、每次停顿100毫秒,现在变成5秒收集一次、每次停顿70毫秒。停顿时间的确在下降,但吞吐量也降下来了。 GCTimeRatio参数的值应当是一个大于0小于100的整数,也就是垃圾收集时间占总时间的比率,相当于是吞吐量的倒数。如果把此参数设置为19,那允许的最大GC时间就占总时间的5% (即1 / (1+19) ) ,默认值为99,就是允许最大1% (即1 / (1+99) ) 的垃圾收集时间。 由于与吞吐量关系密切,Parallel Scavenge收集器也经常被称为"吞吐量优先"收集器。除上述两个参数之外,Parallel Scavenge收集器还有一个参数-XX:+UseAdaptiveSizePolicy值得关注。这是一个开关参数,当这个参数打开之后,就不需要手工指定新生代的大小 (-Xmn) 、Eden与Survivor区的比例 (-XX:SurvivorRatio) 、晋升老年代对象年龄 (-XX:PretenureSizeThreshold) 等细节参数了,虚拟机会根据当前系统的运行情况收集性能监控信息,动态调整这些参数以提供最合适的停顿时间或最大的吞吐量,这种调节方式称为GC自适应的调节策略 (GC Ergonomics) 。如果读者对于收集器运作原理不太了解,手工优化存在困难的时候,使用Parallel Scavenge收集器配合自适应调节策略,把内存管理的调优任务交给虚拟机去完成将是一个很不错的选择。只需要把基本的内存数据设置好 (如-Xmx设置最大堆) ,然后使用MaxGCPauseMillis参数 (更关注最大停顿时间) 或GCTimeRatio参数 (更关注吞吐量) 给虚拟机设立一个优化目标,那具体细节参数的调节工作就由虚拟机完成了。自适应调节策略也是Parallel Scavenge收集器与ParNew收集器的一个重要区别。 HotSpot VM里多个GC有部分共享的代码。有一个分代式GC框架,Serial/Serial Old/ParNew/CMS都在这个框架内;在该框架内的young collector和old collector可以任意搭配使用,所谓的"mix-and-match"。 而ParallelScavenge与G1则不在这个框架内,而是各自采用了自己特别的框架。这是因为新的GC实现时发现原本的分代式GC框架用起来不顺手。请参考官方文档的Collector Styles一段。 ParallelScavenge (PS) 的young collector就如其名字所示,是并行的拷贝式收集器。本来这个young collector就是"Parallel Scavenge"所指,但因为它不兼容原本的分代式GC框架,为了凸显出它是不同的,所以它的young collector带上了PS前缀,全名变成PS Scavenge。对应的,它的old collector的名字也带上了PS前缀,叫做PS MarkSweep。 这个PS MarkSweep默认的实现实际上是一层皮,它底下真正做mark-sweep-compact工作的代码是跟分代式GC框架里的serial old (这个collector名字叫做MarkSweepCompact) 是共用同一份代码的。也就是说实际上PS MarkSweep与MarkSweepCompact在HotSpot VM里是同一个collector实现,包了两张不同的皮;这个collector是串行的。 ...

2017-06-05 · 1 min · 153 words · -

java, jvm GC

java, jvm GC STW (Stop the World) 悬挂指针 查看当前使用的 GC jmx JMX 查看当前JVM使用的GC MBean java.lang.GarbageCollector java code 输出结果跟JMX查看的结果相同. import java.lang.management.GarbageCollectorMXBean; import java.lang.management.ManagementFactory; import java.util.List; public class TestGC { public static void main(String args[]) { List<GarbageCollectorMXBean> l = ManagementFactory.getGarbageCollectorMXBeans(); for (GarbageCollectorMXBean b : l) { System.out.println(b.getName()); } } } prod gc: Copy MarkSweepCompact // 输出 Copy, MarkSweepCompact 代表正在使用单线程的垃圾回收器 -XX:+UseSerialGC 名词解释: 在 GC 的世界里对象指的是通过应用程序利用的数据的集合。是 GC 的基本单位。一般由头 (header) 和域 (field) 构成。 ...

2017-06-04 · 5 min · 866 words · -

Garbage-First G1

Garbage-First G1 http://ifeve.com/%E6%B7%B1%E5%85%A5%E7%90%86%E8%A7%A3g1%E5%9E%83%E5%9C%BE%E6%94%B6%E9%9B%86%E5%99%A8/ G1 GC是Jdk7的新特性之一、Jdk7+版本都可以自主配置G1作为JVM GC选项;作为JVM GC算法的一次重大升级、DK7u后G1已相对稳定、且未来计划替代CMS、所以有必要深入了解下: 不同于其他的分代回收算法、G1将堆空间划分成了互相独立的区块。每块区域既有可能属于O区、也有可能是Y区,且每类区域空间可以是不连续的 (对比CMS的O区和Y区都必须是连续的) 。这种将O区划分成多块的理念源于: 当并发后台线程寻找可回收的对象时、有些区块包含可回收的对象要比其他区块多很多。虽然在清理这些区块时G1仍然需要暂停应用线程、但可以用相对较少的时间优先回收包含垃圾最多区块。这也是为什么G1命名为Garbage First的原因: 第一时间处理垃圾最多的区块。 平时工作中大多数系统都使用CMS、即使静默升级到JDK7默认仍然采用CMS、那么G1相对于CMS的区别在: G1在压缩空间方面有优势 G1通过将内存空间分成区域 (Region) 的方式避免内存碎片问题 Eden, Survivor, Old区不再固定、在内存使用效率上来说更灵活 G1可以通过设置预期停顿时间 (Pause Time) 来控制垃圾收集时间避免应用雪崩现象 G1在回收内存后会马上同时做合并空闲内存的工作、而CMS默认是在STW (stop the world) 的时候做 G1会在Young GC中使用、而CMS只能在O区使用 就目前而言、CMS还是默认首选的GC策略、可能在以下场景下G1更适合: 服务端多核CPU、JVM内存占用较大的应用 (至少大于4G) 应用在运行过程中会产生大量内存碎片、需要经常压缩空间 想要更可控、可预期的GC停顿周期;防止高并发下应用雪崩现象

2017-06-04 · 1 min · 37 words · -

程序,进程, 线程, 内核线程, 轻量级进程, 用户线程

程序,进程, 线程, 内核线程, 轻量级进程, 用户线程 程序 程序用于描述进程要完成的功能,是控制进程执行的指令集; 数据集合是程序在执行时所需要的数据和工作区; 程序控制块(Program Control Block,简称PCB),包含进程的描述信息和控制信息,是进程存在的唯一标志。 进程 动态性:进程是程序的一次执行过程,是临时的,有生命期的,是动态产生,动态消亡的; 并发性:任何进程都可以同其他进程一起并发执行; 独立性:进程是系统进行资源分配和调度的一个独立单位; 结构性:进程由程序、数据和进程控制块三部分组成。 Linux进程类别 虽然我们在区分Linux进程类别, 但是我还是想说Linux下只有一种类型的进程,那就是task_struct,当然我也想说linux其实也没有线程的概念, 只是将那些与其他进程共享资源的进程称之为线程。 一个进程由于其运行空间的不同, 从而有内核线程和用户进程的区分, 内核线程运行在内核空间, 之所以称之为线程是因为它没有虚拟地址空间, 只能访问内核的代码和数据, 而用户进程则运行在用户空间, 但是可以通过中断, 系统调用等方式从用户态陷入内核态。 用户进程运行在用户空间上, 而一些通过共享资源实现的一组进程我们称之为线程组, Linux下内核其实本质上没有线程的概念, Linux下线程其实上是与其他进程共享某些资源的进程而已。但是我们习惯上还是称他们为线程或者轻量级进程 因此, Linux上进程分3种,内核线程 (或者叫核心进程)、用户进程、用户线程, 当然如果更严谨的,你也可以认为用户进程和用户线程都是用户进程。 关于轻量级进程这个概念, 其实并不等价于线程 不同的操作系统中依据其实现的不同, 轻量级进程其实是一个不一样的概念 内核进程 内核中没有进程的概念 不存在的概念, 内核空间的进程共享内核空间的内存, 没有独立的地址空间, 不能算是进程, 所以没有内核进程这个概念. https://www.coder.work/article/6802420 内核中没有进程的概念,所以你的问题没有意义。 Linux 内核可以并且确实创建了完全在内核上下文中运行的线程,但是所有这些线程都运行在相同的地址空间中。尽管相关线程通常具有相关名称,但没有按 PID 对类似线程进行分组。 如果多个内核线程正在处理同一任务或以其他方式共享数据,则它们需要通过锁定或其他并发算法来协调对该数据的访问。当然,内核中不提供 pthreads API,但是可以使用内核互斥锁、等待队列等来获得与 pthread 互斥锁、条件变量等相同的功能。 将这些执行上下文称为“内核线程”是一个相当不错的名字,因为它们非常类似于用户空间进程中的多个线程。它们都共享(内核的)地址空间,但有自己的执行上下文(堆栈、程序计数器等),并且每个都被独立调度并并行运行。另一方面,内核实际上实现了所有漂亮的 POSIX API 抽象(在用户空间中的 C 库的帮助下),因此在该实现的内部我们没有完整的抽象。 内核线程 内核线程一般特指由内核自己启动的只运行在内核态的一些服务进程. 只运行在内核态 ps命令输出里名字带中括号 内核线程也可以叫内核任务,它们周期性地执行例如,磁盘高速缓存的刷新,网络连接的维护,页面的换入换出等等。 内核线程执行的是内核中的函数,而普通进程只有通过系统调用才能执行内核中的函数。 内核线程只运行在内核态,而普通进程既可以运行在用户态,也可以运行在内核态。 因为内核线程指只运行在内核态,因此,它只能使用大于PAGE_OFFSET (3G)的地址空间。另一方面,不管在用户态还是内核态,普通进程可以使用4GB的地址空间。 内核线程是由kernel_thread( )函数在内核态下创建的 内核线程和用户进程的区分, 内核线程运行在内核空间, 之所以称之为线程是因为它没有虚拟地址空间 内核线程就是内核的分身,一个分身可以处理一件特定事情。这在处理异步事件如异步IO时特别有用。内核线程的使用是廉价的,唯一使用的资源就是内核栈和上下文切换时保存寄存器的空间。支持多线程的内核叫做多线程内核(Multi-Threads kernel )。 ...

2017-06-02 · 13 min · 2746 words · -

System.gc(), System.runFinalization()

System.gc(), System.runFinalization() System.gc: 告诉垃圾收集器打算进行垃圾收集,而垃圾收集器进不进行收集是不确定的。只是建议进行回收。 jvm有自己的gc策略,不建议手动调用。 system.gc其实是做一次full gc system.gc会暂停整个进程 system.gc一般情况下我们要禁掉,使用-XX:+DisableExplicitGC system.gc在cms gc下我们通过-XX:+ExplicitGCInvokesConcurrent来做一次稍微高效点的GC(效果比Full GC要好些) system.gc最常见的场景是RMI/NIO下的堆外内存分配等 System.runFinalization() Runs the finalization methods of any objects pending finalization. Calling this method suggests that the Java Virtual Machine expend effort toward running the finalize methods of objects that have been found to be discarded but whose finalize methods have not yet been run. When control returns from the method call, the Java Virtual Machine has made a best effort to complete all outstanding finalizations. ...

2017-06-02 · 1 min · 98 words · -

JNI左中括号[B[C[[C等

‘JNI左中括号[B[C[[C等’ http://blog.csdn.net/qinjuning/article/details/7599796 在Java存在两种数据类型: 基本类型 和 引用类型 。 在JNI的世界里也存在类似的数据类型,与Java比较起来,其范围更具严格性,如下: 1、primitive types ----基本数据类型,如: int、 float 、char等基本类型 2、reference types----引用类型,如: 类、实例、数组。 特别需要注意: 数组 ------ 不管是对象数组还是基本类型数组,都作为reference types存在。 1、primitive types (基本数据类型)映射参见下表: 这些基本数据类型都是可以在Native层直接使用的 。 2、reference types (引用数据类型)映射参见下表 Java类型 Native Type 描述 注意: 1、引用数据类型则不能直接使用,需要根据JNI函数进行相应的转换后,才能使用 2、多维数组(包括二维数组)都是引用类型,需要使用 jobjectArray 类型存取其值 ; 例如: 二维整型数组就是指向一位数组的数组,其声明使用方式如下: print? //获得一维数组 的类引用,即jintArray类型 jclass intArrayClass = env->FindClass("[I"); //构造一个指向jintArray类一维数组的对象数组,该对象数组初始大小为dimion jobjectArray obejctIntArray = env->NewObjectArray(dimion ,intArrayClass , NULL); ...//具体操作 另外,关于引用类型的一个继承关系如下,我们可以对具有父子关系的类型进行转换: 类描述符 类描述符是类的完整名称 (包名+类名) ,将原来的 . 分隔符换成 / 分隔符。 例如: 在java代码中的java.lang.String类的类描述符就是java/lang/String 其实,在实践中,我发现可以直接用该类型的域描述符取代,也是可以成功的。 例如: jclass intArrCls = env->FindClass("java/lang/String") 等同于 jclass intArrCls = env->FindClass("Ljava/lang/String;") 数组类型的描述符则为,则为: [ + 其类型的域描述符 (后文说明) 例如: int [ ] 其描述符为[I float [ ] 其描述符为[F String [ ] 其描述符为[Ljava/lang/String; 域描述符 1、基本类型的描述符已经被定义好了,如下表所示: 2、引用类型的描述符 一般引用类型则为 L + 该类型类描述符 + ; (注意,这儿的分号";"只得是JNI的一部分,而不是我们汉语中的分段,下同) 例如: String类型的域描述符为 Ljava/lang/String; 对于数组,其为 : [ + 其类型的域描述符 + ; int[ ] 其描述符为[I float[ ] 其描述符为[F String[ ] 其描述符为[Ljava/lang/String; Object[ ]类型的域描述符为[Ljava/lang/Object; 多维数组则是 n个[ +该类型的域描述符 , N代表的是几维数组。例如: int [ ][ ] 其描述符为[[I float[ ][ ] 其描述符为[[F 方法描述符 将参数类型的域描述符按照申明顺序放入一对括号中后跟返回值类型的域描述符,规则如下: (参数的域描述符的叠加)返回 类型描述符。对于,没有返回值的,用V(表示void型)表示。举例如下: Java层方法 JNI函数签名 String test ( ) Ljava/lang/String; int f (int i, Object object) (ILjava/lang/Object;)I void set (byte[ ] bytes) ([B)V 在编程时,如果是利用javah工具的话,这些都不需要我们手动编写对应的类型转换,如果不能用javah工具,就只能手动的 进行类型转换了。

2017-06-02 · 1 min · 169 words · -

java8 去除 永久代, metaspace (元空间)

java8 去除 永久代, metaspace (元空间) 在过去 (当自定义类加载器使用不普遍的时候) ,类几乎是"静态的"并且很少被卸载和回收, 因此类也可以被看成"永久的"。另外由于类作为JVM实现的一部分,它们不由程序来创建,因为它们也被认为是"非堆"的内存。 在JDK8之前的 HotSpot虚拟机中,类的这些"永久的"数据存放在一个叫做永久代的区域。永久代一段连续的内存空间,我们在JVM启动之前可以通过设置-XX:MaxPermSize 的值来控制永久代的大小, 32位机器默认的永久代的大小为64M, 64位的机器则为85M。永久代的垃圾回收和老年代的垃圾回收是绑定的,一旦其中一个区域被占满, 这两个区都要进行垃圾回收。但是有一个明显的问题, 由于我们可以通过‑XX:MaxPermSize 设置永久代的大小,一旦类的元数据超过了设定的大小, 程序就会耗尽内存,并出现内存溢出错误(OOM)。 备注: 在JDK7之前的 HotSpot虚拟机中, 纳入字符串常量池的字符串被存储在永久代中, 因此导致了一系列的性能问题和内存溢出错误。 http://www.cnblogs.com/moonandstar08/p/5001914.html 这项改动是很有必要的, 因为对永久代进行调优是很困难的。 永久代中的元数据可能会随着每一次 Full GC发生而进行移动。并且为永久代设置空间大小也是很难确定的, 因为这其中有很多影响因素,比如类的总数,常量池的大小和方法数量等。 同时,HotSpot 虚拟机的每种类型的垃圾回收器都需要特殊处理永久代中的元数据。将元数据从永久代剥离出来, 不仅实现了对元空间的无缝管理, 还可以简化Full GC以及对以后的并发隔离类元数据等方面进行优化。 新增加的 metaspace (元空间) 持久代的空间被彻底地删除了, 它被一个叫元空间的区域所替代了。 持久代删除了之后,很明显, JVM会忽略 PermSize 和 MaxPermSize 这两个参数,还有就是你再也看不到 java.lang.OutOfMemoryError: PermGen error 的异常了。 JDK 8 的 HotSpot JVM 现在使用的是本地内存来表示类的元数据,这个区域就叫做元空间。 元空间存储类的元信息,静态变量和常量池等并入堆中。相当于永久代的数据被分到了堆和元空间中。 元空间的特点: 充分利用了Java语言规范中的好处: 类及相关的元数据的生命周期与类加载器的一致。 每个加载器有专门的存储空间 只进行线性分配 不会单独回收某个类 省掉了GC扫描及压缩的时间 元空间里的对象的位置是固定的 如果GC发现某个类加载器不再存活了,会把相关的空间整个回收掉 元空间的内存分配模型 绝大多数的类元数据的空间都从本地(native)内存中分配 用来描述类元数据的类也被删除了 分元数据分配了多个虚拟内存空间 给每个类加载器分配一个内存块的列表。块的大小取决于类加载器的类型; sun/反射/代理对应的类加载器的块会小一些 归还内存块,释放内存块列表 一旦元空间的数据被清空了,虚拟内存的空间会被回收掉 减少碎片的策略 ...

2017-06-02 · 1 min · 78 words · -

/dev/shm

/dev/shm http://dbua.iteye.com/blog/1271574 1.linux下的/dev/shm是什么? /dev/shm/是linux下一个目录,/dev/shm目录不在磁盘上,而是在内存里,因此使用linux /dev/shm/的效率非常高,直接写进内存。 我们可以通过以下两个脚本来验证linux /dev/shm的性能: [root@db1 oracle]# ls -l linux_11gR2_grid.zip -rw-r-r- 1 oracle dba 980831749 Jul 11 20:18 linux_11gR2_grid.zip [root@db1 oracle]# cat mycp.sh !/bin/sh echo date cp linux_11gR2_grid.zip .. echo date [root@db1 oracle]# ./mycp.sh Fri Jul 15 18:44:17 CST 2011 Fri Jul 15 18:44:29 CST 2011 [root@db1 shm]# df -h Filesystem Size Used Avail Use% Mounted on /dev/mapper/rootvg-lv01 97G 9.2G 83G 10% / /dev/sda1 99M 15M 80M 16% /boot ...

2017-05-31 · 5 min · 979 words · -

sar command

sar command sar -n DEV 1 sar -n TCP,ETCP 1 Cannot open /var/log/sa/sa22: No such file or directory 22 是指当天的日期 这个是由于没有创建那sa22这个文件,这可以通过参数-o让其生成 sar -o 2 3 在对应的/var/log/sa/目录下就有对应的日志文件了 在运行就不会报错了 还有另外一种情况,因为刚刚装上sar所以还没有数据在/var/log/sa/sa22下,因为sar -n DEV 默认是是过去十分钟的系统活动报告。 pacman -S sysstat sar 找出系统瓶颈的利器 sar是System Activity Reporter (系统活动情况报告) 的缩写。sar工具将对系统当前的状态进行取样,然后通过计算数据和比例来表达系统的当前运行状态。它的特点是可以连续对系统取样,获得大量的取样数据;取样数据和分析的结果都可以存入文件,所需的负载很小。sar是目前Linux上最为全面的系统性能分析工具之一,可以从14个大方面对系统的活动进行报告,包括文件的读写情况、系统调用的使用情况、串口、CPU效率、内存使用状况、进程活动及IPC有关的活动等,使用也是较为复杂。 sar是查看操作系统报告指标的各种工具中,最为普遍和方便的;它有两种用法; 追溯过去的统计数据 (默认) 周期性的查看当前数据 12.1. 追溯过去的统计数据 默认情况下,sar从最近的0点0分开始显示数据;如果想继续查看一天前的报告;可以查看保存在/var/log/sysstat/下的sa日志; 使用sar工具查看: $sar -f /var/log/sysstat/sa28 | head sar -r -f /var/log/sysstat/sa28 ../_images/sar1.png 12.2. 查看CPU使用率 sar -u : 默认情况下显示的cpu使用率等信息就是sar -u; ../_images/sar2.png 可以看到这台机器使用了虚拟化技术,有相应的时间消耗; 各列的指标分别是: %user 用户模式下消耗的CPU时间的比例; %nice 通过nice改变了进程调度优先级的进程,在用户模式下消耗的CPU时间的比例 %system 系统模式下消耗的CPU时间的比例; ...

2017-05-30 · 2 min · 291 words · -

busybox

busybox BusyBox 是一个提供了很多简化版的Unix 工具的一个可执行文件, busybox能在很多POSIX环境运行,比如Linux,Android,FreeBSD. BusyBox最初是由布鲁斯·斐伦斯在1996年为Debian GNU/Linux安装光盘而编写 Busybox只提供每个工具程序的简化版 (例如: ls指令不提供排序功能) 。 BusyBox 是一个集成了一百多个最常用linux命令和工具的软件,他甚至还集成了一个http服务器和一个telnet服务器,而所有这一切功能却只有区区1M左右的大小。BusyBox 包含了一些简单的工具,比如: 我们平时用的那些linux命令就好比是分立式的电子元件,而busybox就好比是一个集成电路,把常用的工具和命令集成压缩在一个可执行文件里,功能基本不变,而大小却小很多倍,例如ls、 cat 和 echo等等,还包含了一些更大、更复杂的工具,例如 grep、find、mount 以及telnet。简单的说BusyBox就好像是个大工具箱,它集成压缩了 Linux 的许多工具和命令,也包含了 Android 系统自带的shell。BusyBox 将许多具有共性的小版本的UNIX工具结合到一个单一的可执行文件。这样的集合可以替代大部分常用工具比如: GNU 等工具,BusyBox提供了一个比较完善的环境,可以适用于任何小的或嵌入式系统。在嵌入式linux应用中,busybox有非常广的应用,另外,大多数linux发行版的安装程序中都有busybox的身影,安装linux的时候案ctrl+alt+F2可以切换物理终端,而这个物理终端中的所有命令都是指向busybox的链接.Busybox虽小,但作用确是惊人的,这样就可以基于Busybox制作一张软盘linux。 http://jilili.blog.51cto.com/6617089/1170368 https://busybox.net/ https://en.wikipedia.org/wiki/BusyBox

2017-05-27 · 1 min · 30 words · -

java 监视器

java 监视器 http://ifeve.com/monitors-java-synchronization-mechanism/ 监视器–JAVA同步基本概念 大学有一门课程叫操作系统,学习过的同学应该都记得,监视器是操作系统实现同步的重要基础概念,同样它也用在JAVA的线程同步中,这篇文章用一种类推的思想解释监视器"monitor"。 1.什么是监视器 监视器可以看做是经过特殊布置的建筑,这个建筑有一个特殊的房间,该房间通常包含一些数据和代码,但是一次只能一个消费者(thread)使用此房间, Java-Monitor 当一个消费者(线程)使用了这个房间,首先他必须到一个大厅(Entry Set)等待,调度程序将基于某些标准(e.g. FIFO)将从大厅中选择一个消费者(线程),进入特殊房间,如果这个线程因为某些原因被"挂起",它将被调度程序安排到"等待房间",并且一段时间之后会被重新分配到特殊房间,按照上面的线路,这个建筑物包含三个房间,分别是"特殊房间"、“大厅"以及"等待房间”。 java-monitor-associate-with-object 简单来说,监视器用来监视线程进入这个特别房间,他确保同一时间只能有一个线程可以访问特殊房间中的数据和代码。 2.JAVA中监视器的实现 在JAVA虚拟机中,每个对象(Object和class)通过某种逻辑关联监视器,为了实现监视器的互斥功能,每个对象(Object和class)都关联着一个锁(有时也叫"互斥量"),这个锁在操作系统书籍中称为"信号量",互斥(“mutex “)是一个二进制的信号量。 如果一个线程拥有了某些数据的锁,其他的线程则无法获得锁,直到这个线程释放了这个锁。在多线程中,如果任何时候都是我们自己来写这个信号量,显然不是很方便,幸运的是,JVM为我们自动实现了这些。 为了使数据不被多个线程访问,java 提供了同步块 以及 同步方法两种实现,一旦一段代码被嵌入到一个synchronized关键字中,意味着放入了监视区域,JVM在后台会自动为这段代码实现锁的功能。 3.JAVA的同步代码中,哪一部分是监视器? 我们知道JAVA每个对象(Object/class) 都关联一个监视器,更好的说法应该是每个对象(Object/class)都有一个监视器,对象可以有它自己的临界区,并且能够监视线程序列为了使线程协作,JAVA为提供了wait()和notifyAll以及notify()实现挂起线程,并且唤醒另外一个等待的线程,此外这些方法有三种不同版本: wait(long timeout, int nanos) wait(long timeout) notified by other threads or notified by timeout. notify(all) 这些方法只能在一个同步块或同步方法中被调用,原因是,如果一个方法不需要相互排斥,不需要监测或线程之间协作,每一个线程可以自由访问此方法,那就不需要协作。

2017-05-25 · 1 min · 40 words · -

hbase BufferedMutator

hbase BufferedMutator org.apache.hadoop.hbase.client.BufferedMutator主要用来对HBase的单个表进行操作。它和Put类的作用差不多,但是主要用来实现批量的异步写操作。 BufferedMutator替换了HTable的setAutoFlush(false)的作用。 可以从Connection的实例中获取BufferedMutator的实例。在使用完成后需要调用close()方法关闭连接。对BufferedMutator进行配置需要通过BufferedMutatorParams完成。 MapReduce Job的是BufferedMutator使用的典型场景。MapReduce作业需要批量写入,但是无法找到恰当的点执行flush。BufferedMutator接收MapReduce作业发送来的Put数据后,会根据某些因素 (比如接收的Put数据的总量) 启发式地执行Batch Put操作,且会异步的提交Batch Put请求,这样MapReduce作业的执行也不会被打断。 BufferedMutator也可以用在一些特殊的情况上。MapReduce作业的每个线程将会拥有一个独立的BufferedMutator对象。一个独立的BufferedMutator也可以用在大容量的在线系统上来执行批量Put操作,但是这时需要注意一些极端情况比如JVM异常或机器故障,此时有可能造成数据丢失。

2017-05-22 · 1 min · 12 words · -

堆内内存, 堆外内存

堆内内存, 堆外内存 一、什么是堆外内存 堆内内存 (on-heap memory) 回顾 堆外内存和堆内内存是相对的二个概念,其中堆内内存是我们平常工作中接触比较多的,我们在jvm参数中只要使用-Xms,-Xmx等参数就可以设置堆的大小和最大值,理解jvm的堆还需要知道下面这个公式: 堆内内存 = 新生代+老年代+持久代 如下面的图所示: Paste_Image.png 在使用堆内内存 (on-heap memory) 的时候,完全遵守JVM虚拟机的内存管理机制,采用垃圾回收器 (GC) 统一进行内存管理,GC会在某些特定的时间点进行一次彻底回收,也就是Full GC,GC会对所有分配的堆内内存进行扫描,在这个过程中会对JAVA应用程序的性能造成一定影响,还可能会产生Stop The World。 常见的垃圾回收算法主要有: 引用计数器法 (Reference Counting) 标记清除法 (Mark-Sweep) 复制算法 (Coping) 标记压缩法 (Mark-Compact) 分代算法 (Generational Collecting) 分区算法 (Region) 注: 在这里我们不对各个算法进行深入介绍,感兴趣的同学可以关注我的下一篇关于垃圾回收算法的介绍分享。 堆外内存 (off-heap memory) 介绍 和堆内内存相对应,堆外内存就是把内存对象分配在Java虚拟机的堆以外的内存,这些内存直接受操作系统管理 (而不是虚拟机) ,这样做的结果就是能够在一定程度上减少垃圾回收对应用程序造成的影响。 作为JAVA开发者我们经常用java.nio.DirectByteBuffer对象进行堆外内存的管理和使用,它会在对象创建的时候就分配堆外内存。 DirectByteBuffer类是在Java Heap外分配内存,对堆外内存的申请主要是通过成员变量unsafe来操作,下面介绍构造方法 DirectByteBuffer(int cap) { super(-1, 0, cap, cap); //内存是否按页分配对齐 boolean pa = VM.isDirectMemoryPageAligned(); //获取每页内存大小 int ps = Bits.pageSize(); //分配内存的大小,如果是按页对齐方式,需要再加一页内存的容量 long size = Math.max(1L, (long)cap + (pa ? ps : 0)); //用Bits类保存总分配内存(按页分配)的大小和实际内存的大小 Bits.reserveMemory(size, cap); long base = 0; try { //在堆外内存的基地址,指定内存大小 base = unsafe.allocateMemory(size); } catch (OutOfMemoryError x) { Bits.unreserveMemory(size, cap); throw x; } unsafe.setMemory(base, size, (byte) 0); //计算堆外内存的基地址 if (pa && (base % ps != 0)) { // Round up to page boundary address = base + ps - (base & (ps - 1)); } else { address = base; } cleaner = Cleaner.create(this, new Deallocator(base, size, cap)); att = null; } 注: 在Cleaner 内部中通过一个列表,维护了一个针对每一个 directBuffer 的一个回收堆外内存的 线程对象(Runnable),回收操作是发生在 Cleaner 的 clean() 方法中。 ...

2017-05-17 · 3 min · 470 words · -

CLH队列,CLH锁

CLH队列,CLH锁 CLH的发明人是: Craig,Landin and Hagersten。 CLH锁即 Craig, Landin, and Hagersten (CLH) locks CLH锁是一个自旋锁,能确保无饥饿性,提供先来先服务的公平性。 CLH锁是一种基于链表的可扩展、高性能、公平的自旋锁,申请线程只在本地变量上自旋,它不断轮询前驱的状态,如果发现前驱释放了锁就结束自旋。 CLH算法实现 CLH队列锁表示为QNode对象的链表,QNode中含有一个locked字段,该字段若为true表示该线程需要获取锁,且不释放锁,为false表示线程释放了锁。结点之间是通过隐形的链表相连,之所以叫隐形的链表是因为这些结点之间没有明显的next指针,每个线程通过一个线程局部变量pred指向其前驱,线程通过检测前驱结点的locked域来判断是否轮到自己。如果该域为true,则前驱线程要么已经获得锁要么正在等待锁;如果该域为false,则前驱进程已释放锁,轮到自己了。正常情况下,队列链中只有一个结点的locked域为false。CLHLock上还有一个尾指针,始终指向队列的最后一个结点。 当一个线程调用lock()方法想获得锁时,将自己的locked域置为true,表示该线程不准备释放锁,然后并将自己的结点加入到队列链尾部。最后就是在前驱的locked域上旋转,等待前驱释放锁。当这个线程调用unlock()方法要释放锁时,线程要将自己的locked域置为false,表示已经释放锁,然后将前驱结点作为自己的新结点以便日后访问。 //clh 1 class ClhSpinLock { private final ThreadLocal<Node> prev; private final ThreadLocal<Node> node; private final AtomicReference<Node> tail = new AtomicReference<Node>(new Node()); public ClhSpinLock() { this.node = new ThreadLocal<Node>() { protected Node initialValue() { return new Node(); } }; this.prev = new ThreadLocal<Node>() { protected Node initialValue() { return null; } }; } public void lock() { final Node node = this.node.get(); node.locked = true; // 一个CAS操作即可将当前线程对应的节点加入到队列中, // 并且同时获得了前继节点的引用,然后就是等待前继释放锁 Node pred = this.tail.getAndSet(node); this.prev.set(pred); while (pred.locked) {// 进入自旋 } } public void unlock() { final Node node = this.node.get(); node.locked = false;// 改变状态,让后续线程结束自旋 this.node.set(this.prev.get()); } private static class Node { private volatile boolean locked; } } //clh 2 import java.util.concurrent.atomic.AtomicReferenceFieldUpdater; public class CLHLock { public static class CLHNode { private volatile boolean isLocked = true; // 默认是在等待锁 } @SuppressWarnings("unused" ) private volatile CLHNode tail ; private static final AtomicReferenceFieldUpdater<CLHLock, CLHNode> UPDATER = AtomicReferenceFieldUpdater .newUpdater(CLHLock.class, CLHNode .class , "tail" ); public void lock(CLHNode currentThread) { CLHNode preNode = UPDATER.getAndSet( this, currentThread); if(preNode != null) {//已有线程占用了锁,进入自旋 while(preNode.isLocked ) { } } } public void unlock(CLHNode currentThread) { // 如果队列里只有当前线程,则释放对当前线程的引用 (for GC) 。 if (!UPDATER.compareAndSet(this, currentThread, null)) { // 还有后续线程 currentThread.isLocked = false ;// 改变状态,让后续线程结束自旋 } } } NUMA与SMP SMP ( Symmetric Multi-Processor ) , 对称多处理器结构 对称多处理器结构,指服务器中多个CPU对称工作,每个CPU访问内存地址所需时间相同。其主要特征是共享,包含对CPU,内存,I/O等进行共享。SMP的优点是能够保证内存一致性,缺点是这些共享的资源很可能成为性能瓶颈,随着CPU数量的增加,每个CPU都要访问相同的内存资源,可能导致内存访问冲突,可能会导致CPU资源的浪费。常用的PC机就属于这种。 ...

2017-05-17 · 2 min · 230 words · -

Java8 jdeps

Java8 jdeps http://blog.csdn.net/u013803262/article/details/70570161 Java8中带了一个新的类依赖分析器。 我们可以在Java的安装目录的bin目录下看到jdeps.exe这个文件。 这个工具是用于分析类的依赖关系的。 具体怎么用 可以这样。 找一个目录,下面全是jar文件。那么这样的目录很明显WEB-INF下的lib目录就非常合适。 假设我们有一个web项目,tomcat下的lib有 commons-pool2-2.0.jar jedis-2.5.1.jar 为了使得结构简单点。我们只加入了两个jar包并且jedis包依赖于commons-pool这个包。 我们进入lib目录下运行命令 jdeps *.jar 结果得到 E:\git_tmp\distributedSession\target\distributedSession\WEB-INF\lib>jdeps *.jar commons-pool2-2.0.jar -> F:\Program Files\Java\jdk1.8.0_121\jre\lib\rt.jar commons-pool2-2.0.jar -> 找不到 org.apache.commons.pool2 (commons-pool2-2.0.jar) -> java.io -> java.lang -> java.util -> java.util.concurrent.locks org.apache.commons.pool2.impl (commons-pool2-2.0.jar) -> java.io -> java.lang -> java.lang.management -> java.lang.ref -> java.lang.reflect -> java.security -> java.text -> java.util -> java.util.concurrent -> java.util.concurrent.atomic -> java.util.concurrent.locks -> javax.management -> org.apache.commons.pool2 commons-pool2-2.0.jar org.apache.commons.pool2.proxy (commons-pool2-2.0.jar) -> java.lang -> java.lang.reflect ...

2017-05-17 · 1 min · 151 words · -

Deprecate HTablePool

Deprecate HTablePool https://issues.apache.org/jira/browse/HBASE-6580 http://blog.csdn.net/jiyiqinlovexx/article/details/36526433 http://blog.csdn.net/u010967382/article/details/38046821

2017-05-16 · 1 min · 5 words · -

HBase的WAL机制 WAL (Write-Ahead-Log)

HBase的WAL机制 WAL (Write-Ahead-Log) WAL机制解析 WAL(Write-Ahead Logging)是一种高效的日志算法,几乎是所有非内存数据库提升写性能的不二法门,基本原理是在数据写入之前首先顺序写入日志,然后再写入缓存,等到缓存写满之后统一落盘。之所以能够提升写性能,是因为WAL将一次随机写转化为了一次顺序写加一次内存写。提升写性能的同时,WAL可以保证数据的可靠性,即在任何情况下数据不丢失。假如一次写入完成之后发生了宕机,即使所有缓存中的数据丢失,也可以通过恢复日志还原出丢失的数据。 WAL持久化等级 HBase中可以通过设置WAL的持久化等级决定是否开启WAL机制、以及HLog的落盘方式。WAL的持久化等级分为如下四个等级: SKIP_WAL: 只写缓存,不写HLog日志。这种方式因为只写内存,因此可以极大的提升写入性能,但是数据有丢失的风险。在实际应用过程中并不建议设置此等级,除非确认不要求数据的可靠性。 ASYNC_WAL: 异步将数据写入HLog日志中。 SYNC_WAL: 同步将数据写入日志文件中,需要注意的是数据只是被写入文件系统中,并没有真正落盘。 FSYNC_WAL: 同步将数据写入日志文件并强制落盘。最严格的日志写入等级,可以保证数据不会丢失,但是性能相对比较差。 USER_DEFAULT: 默认如果用户没有指定持久化等级,HBase使用SYNC_WAL等级持久化数据。 用户可以通过客户端设置WAL持久化等级,代码: put.setDurability(Durability. SYNC_WAL ); HLog数据结构 HBase中,WAL的实现类为HLog,每个Region Server拥有一个HLog日志,所有region的写入都是写到同一个HLog。下图表示同一个Region Server中的3个 region 共享一个HLog。当数据写入时,是将数据对<HLogKey,WALEdit>按照顺序追加到HLog中,以获取最好的写入性能。 WAL(Write-Ahead-Log)预写日志是HBase的RegionServer在处理数据插入和删除的过程中用来记录操作内容的一种日志。在每次Put、Delete等一条记录时,首先将其数据写入到RegionServer对应的HLog文件的过程。 客户端往RegionServer端提交数据的时候,会先写WAL日志,只有当WAL日志写成功以后,客户端才会被告诉提交数据成功,如果写WAL失败会告知客户端提交失败,换句话说这其实是一个数据落地的过程。 在一个RegionServer上的所有的Region都共享一个HLog,一次数据的提交是先写WAL,写入成功后,再写memstore。当memstore值到达一定是,就会形成一个个StoreFile (理解为HFile格式的封装,本质上还是以HFile的形式存储的) 。 HLog类 RegionServer内WAL文件与Region的关系图 WAL的实现类是HLog,当一个Region被初始化的时候,一个HLog的实例会作为构造函数的参数传进去。 当Region在处理Put、Delete等更新操作时,可以直接使用该共享的HLog的append方法来落地数据。 Put、Delete在客户端上可以通过setWriteToWAL(false)方法来关闭该操作的日志,这么做虽然可以提升入库速度,但最好别这么做,因为有数据丢失的风险存在。 http://www.zkread.com/article/69288.html http://hbasefly.com/2016/03/23/hbase_writer/

2017-05-09 · 1 min · 40 words · -

锁定读, 一致性非锁定读

锁定读, 一致性非锁定读 http://ju.outofmemory.cn/entry/275548 锁定读 (Locking Reads) 在一个事务中查询数据时,普通的SELECT语句不会对查询的数据进行加锁,其他事务仍可以对查询的数据执行更新和删除操作。因此,InnoDB提供了两种类型的锁定读来保证额外的安全性: - SELECT … LOCK IN SHARE MODE - SELECT … FOR UPDATE SELECT … LOCK IN SHARE MODE: 对读取的行添加S锁,其他事物可以对这些行添加S锁,若添加X锁,则会被阻塞。 SELECT … FOR UPDATE: 会对查询的行及相关联的索引记录加X锁,其他事务请求的S锁或X锁都会被阻塞。 当事务提交或回滚后,通过这两个语句添加的锁都会被释放。 注意: 只有在自动提交被禁用时, SELECT FOR UPDATE 才可以锁定行, 若开启自动提交, 则匹配的行不会被锁定。 一致性非锁定读 (Consistent Nonlocking Reads) MySQL官方文档对弈一致性读的定义: > A consistent read means that InnoDB uses multi-versioning to present to a query a snapshot of the database at a point in time. 就是说: 一致性读意味着InnoDB使用多版本控制来实现一个查询,这个查询所依赖的数据是基于某个时间点的数据库快照。 注意: 是数据库的快照,而不是要查询的那个表的快照。 在事务隔离级别设置为READ COMMITTED和REPEATABLE READ时,默认情况下InnoDB是通过一致性读来处理SELECT语句的。一致性读不会对它访问的表设置任何锁,因此,在一个表上进行一致性读的同时,其他session可以随意修改这些表。 一致性读是使用基于时间点的快照信息来呈现查询结果的读取操作,而不管其他事务在同一时间产生的变更。如果查询的数据已被其他事务改变,InnoDB会基于undo log的内容来重建最初的数据。 两种事务隔离级别下,读取的快照数据的区别: - 在REPEATABLE READ事务隔离级别下,同一事务内的所有一致性读都会读取到该事务中第一个一致性读建立的快照。通过提交当前事务并发起一个新的查询,可以看得到新的快照。 - 在READ COMMITTED事务隔离级别下,同一事务内的每个一致性读总是读取本次一致性读建立的快照。(即,快照的时间点是基于本次一致性读的时间点,也就是最新的数据) 假设当前事务隔离级别为REPEATABLE READ,当发出一个一致性读(也就是一个普通的SELECT语句)时,InnoDB会根据这个查询给此事务分配一个时间点。若其他事务在此时间点之后删除了一行并提交,那么此事务不会看到其他事务产生的影响(即,还是会看到被删除的行)。插入和更新操作也一样。

2017-05-09 · 1 min · 87 words · -

MVCC, Multiversion Concurrency Control, 多版本并发控制

MVCC, Multiversion Concurrency Control, 多版本并发控制 http://donghui.blog.51cto.com/2709336/692586 多版本并发控制技术已经被广泛运用于各大数据库系统中,如 Oracle, MS SQL Server 2005+, Postgresql, Firebird, Maria 等等, 开源数据库 MySQL 中流行的 INNODB 引擎也采用了类似的并发控制技术.本文就将结合实例来解析不同事务隔离等级下 INNODB 的 MVCC 实现原理. 1.1 MVCC 简介 MVCC (Multiversion Concurrency Control), 即多版本并发控制技术, 它使得大部分支持行锁的事务引擎, 不再单纯的使用行锁来进行数据库的并发控制, 取而代之的是, 把数据库的行锁与行的多个版本结合起来, 只需要很小的开销, 就可以实现非锁定读, 从而大大提高数据库系统的并发性能. 1.2 实现原理 MVCC 可以提供基于某个时间点的快照, 使得对于事务看来, 总是可以提供与事务开始时刻相一致的数据, 而不管这个事务执行的时间有多长. 所以在不同的事务看来, 同一时刻看到的相同行的数据可能是不一样的, 即一个行可能有多个版本. 是否听起来不可思议呢? 原来, 为了实现 mvcc, innodb 对每一行都加上了两个隐含的列, 其中一列存储行被更新的"时间", 另外一列存储行被删除的"时间". 但是 innodb 存储的并不是绝对的时间, 而是与时间对应的数据库系统的版本号, 每当一个事务开始的时候, innodb 都会给这个事务分配一个递增的版本号, 所以版本号也可以被认为是事务号. 对于每一个"查询"语句, innodb 都会把这个查询语句的版本号同这个查询语句遇到的行的版本号进行对比, 然后结合不同的事务隔离等级, 来决定是否返回该行. ...

2017-05-09 · 5 min · 859 words · -

Locked ownable synchronizers

Locked ownable synchronizers An ownable synchronizer is a synchronizer that may be exclusively owned by a thread and uses AbstractOwnableSynchronizer (or its subclass) to implement its synchronization property. ReentrantLock and ReentrantReadWriteLock are two examples of ownable synchronizers provided by the platform. http://stackoverflow.com/questions/41300520/what-is-locked-ownable-synchronizers-in-thread-dump

2017-05-05 · 1 min · 42 words · -