Java 项目优化实战

来源:互联网 发布:rcpp软件 编辑:程序博客网 时间:2024/05/20 02:56



  • 1 Visual VM

  • 2 优化一

    • 2.1 背景

    • 2.2 原实现

    • 2.3 剖析

    • 2.3 方案

    • 2.4 核心代码

  • 3 优化二

    • 3.1 背景

    • 3.2 原实现

    • 3.3 剖析

    • 3.4 方案 & 代码

  • 4 成果


1 Visual VM



项目中的某一个接口,在某一场景下(数据量大),性能让人难以忍受。

那么如何有什么工具可以定位引发性能问题的代码呢?其实有很多,这里我们使用 Visual VM。



Visual VM 是一款用来分析 Java 应用的图形工具,能够对 Java 应用程序做性能分析和调优。如果你使用的 java 7 或者 java 8,那么可以直接在 JDK 的 bin 目录找到该工具,名称为 jvisualvm。当然也可以在官网上自行下载。

使用 Visual VM 分析某个接口的性能的方法如下:



结果显示如下:



通过上图,我们可以看到比较耗时的方法为 resolveBytePosition 和 rest,getFile 和 currentUser 是网络请求,暂不考虑。



2 优化一



2.1 背景

首先拿 resolveBytePosition 方法开刀。为了能更容易的解释 resolveBytePosition 的用途,举个例子。

给定一个字符串 chars 与该字符串的 UTF-8 二进制数组(空格用来隔开字符数据,实际并不存在):




resolveBytePosition 用来解决给定一个 bytes 的偏移 bytePos 计算 chars 中的偏移 charPos 的问题。比如:



如果使用 array[start:] 表示从下标 start 开始截取数组元素至末尾组成的新数组,那么则有:

 


举例:




2.2 原实现

明白了 resolveBytePosition 的作用,看一下它的实现




该解法简单粗暴,能够准确的计算出结果,但是缺点显而易见,频繁的构建字符串,对性能造成了极大的影响。通过 Visual VM 可以证实我们的推论,通过点击快照,查看更详细的方法调用耗时。



2.3 剖析

为了更方便的剖析问题,我们绘制如下表格,用来展示每一个字符的 UTF-8 以及 Unicode 的二进制数据:




接着我们将字节数据转换为字节长度:




Java中的使用 char 来表示 Unicode,char 的长度为 2 个字节,因此一个 char 足以表示示例中的任何一个字符。

我们使用一个单元格表示一个 byte(UTF-8)或一个char(Unicode),并对单元格编号,得到下表:




可以得出下面对应关系:




2.4 方案

进行到这一步,高效的算法已经呼之欲出了。算法如下:

把字符 UTF-8 数据的二进制长度不为 1 的称为特征点。除特征点外,每个字符都是一个字节长度。记下所有特征点的对应关系,对于给定的 bytePos,都可以根据公式计算得到 charPos。

公式为:




举例:

则本实例中有两个特征点 一、个,记作:




如果给定 bytePos 10, 首先找到前一个特征点的对应关系 9(preBytePos) -> 5(preCharPos), 根据公式得出 (10 - 9) + 5 = 6。

2.4 核心代码

该算法还有一个比较关键的问题要解决,即高效的计算一个 char 的字节长度。计算 char 的字节长度的算法参考了 StackOverflow。







3 优化二



3.1 背景

接下来解决第二个函数 rest。该函数的功能是得到 JsonArray(gson) 的除第一个元素外的所有元素。

由于 rest 是在一个递归函数中被调用且递归栈很深,因此如果 rest 实现的不够高效,其影响会被成倍放大。


3.2 原实现




3.3 剖析

通过调试发现 JsonArray 中存储了相当大的数据,对于频繁调用的场景,每次都对其重新构建明显不是一个明智的选择。
通过查看返回的 JsonArray 使用情况,我们得到了另一条线索:仅仅使用里面的数据,而不涉及修改。

考虑到 JsonArray 被实现成 final,最后方案确定为实现一个针对 rest 这种需求定制的代理类。


3.4 方案 & 代码

代理类 JsonArrayWrapper 分别对 first、rest、foreach 等功能进行了实现。







4 成果



经过这两个主要的优化,就解决了代码中的性能问题,成果如下图所示:





晓蕾已认证QQ478987009小酋长
  • 1 Visual VM

  • 2 优化一

    • 2.1 背景

    • 2.2 原实现

    • 2.3 剖析

    • 2.3 方案

    • 2.4 核心代码

  • 3 优化二

    • 3.1 背景

    • 3.2 原实现

    • 3.3 剖析

    • 3.4 方案 & 代码

  • 4 成果


1 Visual VM



项目中的某一个接口,在某一场景下(数据量大),性能让人难以忍受。

那么如何有什么工具可以定位引发性能问题的代码呢?其实有很多,这里我们使用 Visual VM。



Visual VM 是一款用来分析 Java 应用的图形工具,能够对 Java 应用程序做性能分析和调优。如果你使用的 java 7 或者 java 8,那么可以直接在 JDK 的 bin 目录找到该工具,名称为 jvisualvm。当然也可以在官网上自行下载。

使用 Visual VM 分析某个接口的性能的方法如下:



结果显示如下:



通过上图,我们可以看到比较耗时的方法为 resolveBytePosition 和 rest,getFile 和 currentUser 是网络请求,暂不考虑。



2 优化一



2.1 背景

首先拿 resolveBytePosition 方法开刀。为了能更容易的解释 resolveBytePosition 的用途,举个例子。

给定一个字符串 chars 与该字符串的 UTF-8 二进制数组(空格用来隔开字符数据,实际并不存在):




resolveBytePosition 用来解决给定一个 bytes 的偏移 bytePos 计算 chars 中的偏移 charPos 的问题。比如:



如果使用 array[start:] 表示从下标 start 开始截取数组元素至末尾组成的新数组,那么则有:

 


举例:




2.2 原实现

明白了 resolveBytePosition 的作用,看一下它的实现




该解法简单粗暴,能够准确的计算出结果,但是缺点显而易见,频繁的构建字符串,对性能造成了极大的影响。通过 Visual VM 可以证实我们的推论,通过点击快照,查看更详细的方法调用耗时。



2.3 剖析

为了更方便的剖析问题,我们绘制如下表格,用来展示每一个字符的 UTF-8 以及 Unicode 的二进制数据:




接着我们将字节数据转换为字节长度:




Java中的使用 char 来表示 Unicode,char 的长度为 2 个字节,因此一个 char 足以表示示例中的任何一个字符。

我们使用一个单元格表示一个 byte(UTF-8)或一个char(Unicode),并对单元格编号,得到下表:




可以得出下面对应关系:




2.4 方案

进行到这一步,高效的算法已经呼之欲出了。算法如下:

把字符 UTF-8 数据的二进制长度不为 1 的称为特征点。除特征点外,每个字符都是一个字节长度。记下所有特征点的对应关系,对于给定的 bytePos,都可以根据公式计算得到 charPos。

公式为:




举例:

则本实例中有两个特征点 一、个,记作:




如果给定 bytePos 10, 首先找到前一个特征点的对应关系 9(preBytePos) -> 5(preCharPos), 根据公式得出 (10 - 9) + 5 = 6。

2.4 核心代码

该算法还有一个比较关键的问题要解决,即高效的计算一个 char 的字节长度。计算 char 的字节长度的算法参考了 StackOverflow。







3 优化二



3.1 背景

接下来解决第二个函数 rest。该函数的功能是得到 JsonArray(gson) 的除第一个元素外的所有元素。

由于 rest 是在一个递归函数中被调用且递归栈很深,因此如果 rest 实现的不够高效,其影响会被成倍放大。


3.2 原实现




3.3 剖析

通过调试发现 JsonArray 中存储了相当大的数据,对于频繁调用的场景,每次都对其重新构建明显不是一个明智的选择。
通过查看返回的 JsonArray 使用情况,我们得到了另一条线索:仅仅使用里面的数据,而不涉及修改。

考虑到 JsonArray 被实现成 final,最后方案确定为实现一个针对 rest 这种需求定制的代理类。


3.4 方案 & 代码

代理类 JsonArrayWrapper 分别对 first、rest、foreach 等功能进行了实现。







4 成果



经过这两个主要的优化,就解决了代码中的性能问题,成果如下图所示:


java新人学习群 : 202250194

晓蕾已认证QQ478987009小酋长
  • 1 Visual VM

  • 2 优化一

    • 2.1 背景

    • 2.2 原实现

    • 2.3 剖析

    • 2.3 方案

    • 2.4 核心代码

  • 3 优化二

    • 3.1 背景

    • 3.2 原实现

    • 3.3 剖析

    • 3.4 方案 & 代码

  • 4 成果


1 Visual VM



项目中的某一个接口,在某一场景下(数据量大),性能让人难以忍受。

那么如何有什么工具可以定位引发性能问题的代码呢?其实有很多,这里我们使用 Visual VM。



Visual VM 是一款用来分析 Java 应用的图形工具,能够对 Java 应用程序做性能分析和调优。如果你使用的 java 7 或者 java 8,那么可以直接在 JDK 的 bin 目录找到该工具,名称为 jvisualvm。当然也可以在官网上自行下载。

使用 Visual VM 分析某个接口的性能的方法如下:



结果显示如下:



通过上图,我们可以看到比较耗时的方法为 resolveBytePosition 和 rest,getFile 和 currentUser 是网络请求,暂不考虑。



2 优化一



2.1 背景

首先拿 resolveBytePosition 方法开刀。为了能更容易的解释 resolveBytePosition 的用途,举个例子。

给定一个字符串 chars 与该字符串的 UTF-8 二进制数组(空格用来隔开字符数据,实际并不存在):




resolveBytePosition 用来解决给定一个 bytes 的偏移 bytePos 计算 chars 中的偏移 charPos 的问题。比如:



如果使用 array[start:] 表示从下标 start 开始截取数组元素至末尾组成的新数组,那么则有:

 


举例:




2.2 原实现

明白了 resolveBytePosition 的作用,看一下它的实现




该解法简单粗暴,能够准确的计算出结果,但是缺点显而易见,频繁的构建字符串,对性能造成了极大的影响。通过 Visual VM 可以证实我们的推论,通过点击快照,查看更详细的方法调用耗时。



2.3 剖析

为了更方便的剖析问题,我们绘制如下表格,用来展示每一个字符的 UTF-8 以及 Unicode 的二进制数据:




接着我们将字节数据转换为字节长度:




Java中的使用 char 来表示 Unicode,char 的长度为 2 个字节,因此一个 char 足以表示示例中的任何一个字符。

我们使用一个单元格表示一个 byte(UTF-8)或一个char(Unicode),并对单元格编号,得到下表:




可以得出下面对应关系:




2.4 方案

进行到这一步,高效的算法已经呼之欲出了。算法如下:

把字符 UTF-8 数据的二进制长度不为 1 的称为特征点。除特征点外,每个字符都是一个字节长度。记下所有特征点的对应关系,对于给定的 bytePos,都可以根据公式计算得到 charPos。

公式为:




举例:

则本实例中有两个特征点 一、个,记作:




如果给定 bytePos 10, 首先找到前一个特征点的对应关系 9(preBytePos) -> 5(preCharPos), 根据公式得出 (10 - 9) + 5 = 6。

2.4 核心代码

该算法还有一个比较关键的问题要解决,即高效的计算一个 char 的字节长度。计算 char 的字节长度的算法参考了 StackOverflow。







3 优化二



3.1 背景

接下来解决第二个函数 rest。该函数的功能是得到 JsonArray(gson) 的除第一个元素外的所有元素。

由于 rest 是在一个递归函数中被调用且递归栈很深,因此如果 rest 实现的不够高效,其影响会被成倍放大。


3.2 原实现




3.3 剖析

通过调试发现 JsonArray 中存储了相当大的数据,对于频繁调用的场景,每次都对其重新构建明显不是一个明智的选择。
通过查看返回的 JsonArray 使用情况,我们得到了另一条线索:仅仅使用里面的数据,而不涉及修改。

考虑到 JsonArray 被实现成 final,最后方案确定为实现一个针对 rest 这种需求定制的代理类。


3.4 方案 & 代码

代理类 JsonArrayWrapper 分别对 first、rest、foreach 等功能进行了实现。







4 成果



经过这两个主要的优化,就解决了代码中的性能问题,成果如下图所示:

0 0