0%

HashMap是基于哈希表实现的一个Key-Value的容器,在Java中应用相当的广泛,并且在JDK8中做了一个重大的改变,在JDK8之前,采用数组+链表的形式存储数据,不过考虑到链表$O(n)$的查询性能,自JDK8开始引入了红黑树的结构进行优化,形成了数组+链表+红黑树形式。

本文相关的HashMap源码都是基于JDK8。

HashMap的数据结构及继承关系如下:

阅读全文 »

Linux - 用户空间与内核空间简述一文的最后我们举了一个例子,来介绍读取一个文件并通过socket发送给用户的过程中,数据在Linux中经历了怎样的传递。例子中指出数据会经过2次用户空间与内核之间的拷贝:

  • 内核读取文件数据之后,从内核空间拷贝到用户空间;
  • 调用socket的write时,将数据从用户空间拷贝到内核空间。

以及4次上下文切换:

  • 调用read读取文件时,从用户态切换到内核态;
  • 读取完成之后,切换回来;
  • 调用write发送数据时,从用户态切换到内核态;
  • 发送完成之后,切换回来。

如果你只需要实现一个简单的,不需要考虑高性能的业务,那么到这一步基本上就完成了,但是如果你做的是一个有一定性能要求的框架,比如Netty,Kafka,MQ等等,那么这里还是有优化空间的,优化点就在于本文要介绍的zero-copy(零拷贝)技术。

阅读全文 »

copy-on-write,写时拷贝,是计算机程序设计领域的一种优化策略,其核心思想是,当有多个调用者都需要请求相同资源时,一开始资源只会有一份,多个调用者共同读取这一份资源,当某个调用者需要修改数据的时候,才会分配一块内存,将数据拷贝过去,供这个调用者使用,而其他调用者依然还是读取最原始的那份数据。每次有调用者需要修改数据时,就会重复一次拷贝流程,供调用者修改使用。

使用copy-on-write可以避免或者减少数据的拷贝操作,极大的提高性能,其应用十分广泛,例如Linux的fork调用,Linux的文件管理系统,一些数据库服务,Java中的CopyOnWriteArrayList,C++98/C++03中的std::string等等。

阅读全文 »

阿里巴巴的Java代码规范中,对于线程池的使用有这么一条强制规范:

为什么使用Executors创建的线程池会导致OOM?怎样才是正确使用ThreadPoolExecutor创建线程池的姿势?

阅读全文 »

G1(Garbage First) GC,是JDK9中的默认收集器,在实现高吞吐量的同时,尽可能的满足垃圾收集暂停时间的要求。G1的设计目标是取代CMS收集器(CMS收集器在JDK9中已经被废弃),与CMS相比,G1在以下方面表现的更出色:

  • G1是一个有整理内存过程的垃圾收集器,不会产生很多内存碎片;
  • G1的STW(stop the world)耗时更可控,G1在停顿时间上添加了预测机制,用户可以指定期望停顿时间。
阅读全文 »

CMS(ConcMarkSweep)GC,是GC发展史上,第一款支持“并发”的收集器。

串行收集器:GC过程中,需要STW(stop the word),并且只有一个线程在执行收集工作,比如Serial GC;

并行收集器:GC过程中,需要STW,并且开启多个线程同时执行收集工作,比如Parallel GC;

并发收集器:GC过程中,不需要全程STW,GC线程和用户线程是可以同时工作的,比如CMS GC。

使用-XX:+UseConcMarkSweepGC参数来启用ParNew,CMS进行垃圾收集,其中:

  • ParNew GC:负责Young GC;

  • CMS GC:负责Old GC,并且在进行Old GC的时候,GC线程绝大部分时间可以跟用户线程并行执行;

    注意,其他分代收集的GC组合中,基本上都是一款Young GC搭配一款Full GC,而CMS GC是只收集老年代。

    再注意,这里说的CMS GC是只收集老年代特指标准的Background模式的CMS GC,Foreground模式的CMS GC还是会收集整个堆的,后面会讲这二者的区别,这里大家还是先认为标准的CMS GC只收集老年代。

CMS GC的优势就是在进行老年代收集的时候,只有两次很短的STW,可以大大降低应用的暂停时间。

阅读全文 »

Parallel GC这个套装,有两种组合:

  • 使用-XX:+UseParallelGC参数来启用Parallel Scavenge和PSMarkSweep(Serial Old)收集器组合进行垃圾收集;
  • 使用-XX:+UseParallelOldGC参数来启用Parallel Scavenge和Parallel Old收集器组合进行垃圾收集。

ParallelGC起源

在介绍Parallel GC套装前,先简单介绍一下其开发历史,有助于理解Parallel GC的逻辑。关于Parallel GC的发展史,详细版见R大的回复

阅读全文 »

Serial GC,串行垃圾收集器,在Java语言的发展历程中,可以算是一个历史很悠久的垃圾回收器了。

虽然说随着硬件的迅猛发展,不断出现了更适应高性能服务的垃圾回收器,JDK11推出的ZGC,JDK12推出的Shenandoah GC,更使开发人员比以往任何时候都接近无暂停(STW,stop the word)时间。在这个时候使用串行垃圾收集器显得有点格格不入,不过这并不影响我们通过它去了解收集器的一些基本知识。

阅读全文 »

在C语言中,使用malloc()分配对象之后,最终需要调用free()进行对象回收,否则会出现内存泄露。在C++11以前,也是同样的操作,C++11之后推出了智能指针,通过引用计数的方式,使得代码编写者可以不用主动关心对象回收的问题。

在Java语言中,同样也不用关心主动回收对象的问题,Java提供了一套完整的垃圾回收(GC,Garbage Collection)机制,自动辅助程序进行内存回收,当然如果代码编写不规范,或者参数配置有误的话,也会加重GC的负担,对性能产生比较大的影响。

在食堂吃完饭,端餐盘去回收区的是C程序员,不端盘子直接走的是Java程序员。

阅读全文 »

在单实例的服务中,可以通过加锁的方式处理并发业务,例如synchronized,ReentrantLock等等,但是在分布式环境中,则需要依赖第三方的服务来辅助实现分布式锁。

Redis由于其超高的性能,成为支持分布式锁的主流框架之一,网上已有比较成熟的文章分析其实现方式,本文也只是以作者自己的角度梳理一遍,并没有什么新鲜的知识点。

设计一个Redis的分布式锁,至少需要满足如下两个特点:

  • 互斥性:同时只能有一个客户端能获取锁;
  • 锁的正确释放:锁要么到期自动释放,要么由获取锁的客户端释放,不能以其他方式被释放;
阅读全文 »