Java 9中的GC调优基础

在经过了几次跳票之后,Java 9终于在原计划日期的整整一年之后发布了正式版。Java 9引入了很多新的特性,除了闪瞎眼的Module SystemREPL,最重要的变化我认为是默认GC(Garbage Collector)修改为新一代更复杂、更全面、性能更好的G1(Garbage-First)。JDK的维护者在GC选择上一直是比较保守的,G1从JDK 1.6时代就开始进入开发者的视野,直到今天正式成为Hotspot的默认GC,也是走了很长的路。

本文将主要讲解GC调优需要知道的一些基础知识,会涉及到一些GC的实现细节,但不会对实现细节做很全面的阐述,如果你看完本文之后,能对GC有一个大致的认识,那本文的写作目的也就达到了。由于在这次写作过程中,恰逢Java 9正式版发布,之前都是依赖Java 8的文档写的,如果有不正确的地方还望指正。本文将包含以下内容:

  1. GC的作用范围
  2. GC负责的事情
  3. JVM中的4种GC
  4. G1的一些细节
  5. 使用Java 9正式版对G1进行测试
  6. 一些简单的GC调优方法

KK笔记:kknotes.com
本文链接地址: Java 9中的GC调优基础

转载须以超链接形式标明文章原始出处和作者信息及版权声明

Continue Reading

JDK解构 – Java中的引用和动态代理的实现

我们知道,动态代理(这里指JDK的动态代理)与静态代理的区别在于,其真实的代理类是动态生成的。但具体是怎么生成,生成的代理类包含了哪些内容,以什么形式存在,它为什么一定要以接口为基础?

如果去看动态代理的源代码(java.lang.reflect.Proxy),会发现其原理很简单(真正二进制类文件的生成是在本地方法中完成,源代码中没有),但其中用到了一个缓冲类java.lang.reflect.WeakCache<ClassLoader, Class<?>[], Class<?>>,这个类用到了弱引用来构建。

在JDK的3个特殊引用中,弱引用是使用范围最广的,它的特性也最清晰,相对而言,其他两种逻辑稍显晦涩,源码中的注释也语焉不详。本文将简单介绍几种引用的行为特征,然后分析一下弱引用的一些实际应用场景,其中包含了动态代理中的实现。本文将包含以下内容:

  1. JDK中的引用类型
  2. 不同引用类型对GC行为的影响
  3. 引用类型的实现
  4. ThreadLocal对弱引用的使用
  5. 动态代理对弱引用的实现
  6. 虚引用如何导致内存泄漏

KK笔记:kknotes.com
本文链接地址: JDK解构 – Java中的引用和动态代理的实现

转载须以超链接形式标明文章原始出处和作者信息及版权声明

Continue Reading

Java线程池的理论与实践

Doug Lea

前段时间公司里有个项目需要进行重构,目标是提高吞吐量和可用性,在这个过程中对原有的线程模型和处理逻辑进行了修改,发现有很多基础的多线程的知识已经模糊不清,如底层线程的运行情况、现有的线程池的策略和逻辑、池中线程的健康状况的监控等,这次重新回顾了一下,其中涉及大量java.util.concurrent包中的类。本文将会包含以下内容:

  1. Java中的Thread与操作系统中的线程的关系
  2. 线程切换的各种开销
  3. ThreadGroup存在的意义
  4. 使用线程池减少线程开销
  5. Executor的概念
  6. ThreadPoolExecutor中的一些具体实现
  7. 如何监控线程的健康

KK笔记:kknotes.com
本文链接地址: Java线程池的理论与实践

转载须以超链接形式标明文章原始出处和作者信息及版权声明

Continue Reading

从I/O模型到Netty(三)

Netty

零、写在前面

本文虽然是讲Netty,但实际更关注的是Netty中的NIO的实现,所以对于Netty中的OIO(Old I/O)并没有做过多的描述,或者说根本只字未提,所以本文中所述的所有实现细节都是基于NIO版本的。

Netty作为一个已经发展了十多年的框架,已然非常成熟了,其中有大量的细节是普通使用者不知道或者不关心的,所以本文难免有遗漏或者纰漏的地方,如果你发现了请告知。

本文不涉及Netty5的部分。

虽然这一节叫「写在前面」,但实际上上最后写的。

KK笔记:kknotes.com
本文链接地址: 从I/O模型到Netty(三)

转载须以超链接形式标明文章原始出处和作者信息及版权声明

Continue Reading

从I/O模型到Netty(二)

旧瓶装新酒,Java NIO

在上一篇文章中对于I/O模型已经讲的比较清楚了,在I/O密集型应用中使用Reactor模式可以明显提高系统的性能(我们这里谈到的性能很大程度上指的是吞吐量),但是在具体的开发过程中模式还是要落地成真实的代码,使用传统的I/O库肯定是不行的,在Java中需要使用java.nio包下的库。

虽然是讲NIO的实现,但本文将不会把所有Java NIO中的主要API全部过一遍,而是通过例子理清NIO到底可以做什么事情。

本文中提到的JDK源代码都可以在%JAVA_HOME%/jre/lib/rt.jar中看到。

KK笔记:kknotes.com
本文链接地址: 从I/O模型到Netty(二)

转载须以超链接形式标明文章原始出处和作者信息及版权声明

Continue Reading

从I/O模型到Netty(一)

如何使用咖啡读文件
I/O是任何一个程序设计者都无法忽略的存在,很多高级编程语言都在尝试使用巧妙的设计屏蔽I/O的实际存在,减小它对程序的影响,但是要真正的理解并更好运用这些语言,还是要搞清楚I/O的一些基本理念。本文将从最基本的I/O概念开始,试图理清当前I/O处理存在的问题和与之对应一些手段及背后的思想。

本来这是上个月在公司内部做的一次关于NIO的分享,发现很多概念可能当时理解的很清楚,过了一段时间就会感到模糊了。在这里整理一下,以备以后查看,同时也将作为另一个系列的开端。

由于篇幅限制,本文将只包含I/O模型到Reactor的部分,下一篇会继续讲到Netty和Dubbo中的I/O。本文包含以下内容:

  1. 五种典型的I/O模型
  2. 同步&异步、阻塞&非阻塞的概念
  3. Reactor & Proactor
  4. Reactor的启发

KK笔记:kknotes.com
本文链接地址: 从I/O模型到Netty(一)

转载须以超链接形式标明文章原始出处和作者信息及版权声明

Continue Reading

MyBatis在Spring环境下的事务管理

MyBatis & Spring

MyBatis的设计思想很简单,可以看做是对JDBC的一次封装,并提供强大的动态SQL映射功能。但是由于它本身也有一些缓存、事务管理等功能,所以实际使用中还是会碰到一些问题——另外,最近接触了JFinal,其思想和Hibernate类似,但要更简洁,和MyBatis的设计思想不同,但有一点相同:都是想通过简洁的设计最大限度地简化开发和提升性能——说到性能,前段时间碰到两个问题:

  1. 在一个上层方法(DAO方法的上层)内删除一条记录,然后再插入一条相同主键的记录时,会报主键冲突的错误。
  2. 某些项目中的DAO方法平均执行时间会是其他一些项目中的 2倍 。

第一个问题是偶尔会出现,在实验环境无论如何也重现不了,经过分析MyBatis的逻辑,估计是两个DAO分别拿到了两个不同的Connection,第二个语句比第一个更早的被提交,导致了主键冲突,有待进一步的分析和验证。对于第二个问题,本文将尝试通过分析源代码和实验找到它的root cause,主要涉及到以下内容:

  1. 问题描述与分析
  2. MyBatis在Spring环境下的载入过程
  3. MyBatis在Spring环境下事务的管理
  4. 实验验证

KK笔记:kknotes.com
本文链接地址: MyBatis在Spring环境下的事务管理

转载须以超链接形式标明文章原始出处和作者信息及版权声明

Continue Reading

MyBatis完全使用指南

MyBatis

MyBatis作为一个轻量的SQL映射框架,确实很简单,但是知识点挺多,实际使用中还是会有时想不起来某个标签该怎么写,所以整理了这篇文章,以备查询。由于MyBatis如此简单,使得这一篇文章基本把实际使用中常碰到的事情都涵盖了,包括:

  1. MyBatis中的一些概念
  2. MyBatis包含的内容
  3. SQL映射
  4. 动态SQL

KK笔记:kknotes.com
本文链接地址: MyBatis完全使用指南

转载须以超链接形式标明文章原始出处和作者信息及版权声明

Continue Reading

如何写出优雅的函数(Clean Code读书笔记之二)

第三章讲了在写函数时应该注意的事情,作者首先拿一个开源的测试工具(Fitnesse)来举了一个例子,来说明好的函数该是什么样子。原则上其实和上一篇中讲到的命名的一些原则很相似,就是一个名字要是能够自解释的,当然这一章还会讲到很多新的东西,这里拿这个函数作为一个引子。

//代码2-1
public static String renderPageWithSetupsAndTeardowns(PageData pageData, boolean isSuite)throws Exception{
    boolean isTestPage = pageData.hasAttribute("Test");
    if(isTestPage){
        WikiPage testPage = pageData.getWikiPage();
        StringBuffer newPageContent = new StringBuffer();
        includeSetupPages(test Page, newPageContent, isSuite);
        newPageContent.append(pageData.getContent());
    }
}

KK笔记:kknotes.com
本文链接地址: 如何写出优雅的函数(Clean Code读书笔记之二)

转载须以超链接形式标明文章原始出处和作者信息及版权声明

Continue Reading

命名的艺术(clean code阅读笔记之一)

本文是「Clean Code」(英文版)第二章的读书笔记。

第二章简单地列举了一些命名规则,我们在coding的时候会不断地对我们的变量、函数、参数、类、package,甚至源文件、和包含源文件的目录等等进行命名,这里是简单的几个命名规则能帮助你更好地对这些命名。

1. 使用名副其实(Intention-Revealing)的名字

  • 不要定义无意义的名字
  • 不要使用magic numbers
  • 例子:
    // 坏代码例子
    public List<int[]> getThem() {
        List<int[]> list1 = new ArrayList<int[]>();
        for (int[] x : theList)
            if (x[0] == 4) list1.add(x);
        return list1;
    }
    
    //不是很坏的代码
    public List<int[]> getFlaggedCells() {
        List<int[]> flaggedCells = new ArrayList<int[]>();
        for (int[] cell : gameBoard)
            if (cell[STATUS_VALUE] == FLAGGED)
                flaggedCells.add(cell);
        return flaggedCells;
    }
    
    //好代码
    public List<Cell> getFlaggedCells() {
        List<Cell> flaggedCells = new ArrayList<Cell>();
        for (Cell cell : gameBoard)
            if (cell.isFlagged())
                flaggedCells.add(cell);
        return flaggedCells;
    }
    

KK笔记:kknotes.com
本文链接地址: 命名的艺术(clean code阅读笔记之一)

转载须以超链接形式标明文章原始出处和作者信息及版权声明

Continue Reading