【Java应用服务体系】「序章入门」全方位盘点和总结调优技术专题指南

简介: 【Java应用服务体系】「序章入门」全方位盘点和总结调优技术专题指南

专题⽬标

本系列专题的目标是希望可以帮助读者们系统和全访问掌握应⽤系统调优的思路与方案以及相关的调优工具的使用,虽然未必会覆盖目前的所有的问题场景,但是还是提供了较为丰富的案例和调优理论,会帮助大家打开思维去⽀撑系统服务体系优化能力。

适合人员

Java相关的开发人员、系统架构师、数据库DB人员以及运维人员等

什么是调优

调优手段就是让计算机的硬件或软件在正常地⼯作基础上,非常出色的发挥其应有的性能,并且将所承担的负担降低到最低的技术手段。在Java应用服务体系中有大致可以分为5个维度的调优方向。

调优技术的五个维度

  • 应⽤⾃身的调优
  • 运⾏环境的调优(JVM的调优)
  • 存储上的调优(数据库的调优)
  • 操作系统的调优
  • 架构上的调优

如下图所示。



一般从上到下系统优化的层面成本越来越高,而从下到上系统优化层面的成本越来月底,而且难度也适当下降,建议自下而上的去进行调优规划。

调优技术的四条准则

借助监控预防问题、发现问题,监控 + 告警

采用监控和预防的手段去实现提前发现问题:zabbix、promethus等等

借助⼯具定位问题

问题排查工具使用机制

定期复盘,防⽌同类问题再现

定期进行排查和复盘相关的代码问题,加深我们对问题的印象以及防止问题再次发生

定好规范,⼀定程度上规避问题

制定标准规范,约束问题的发生。

调优的原则

有问题,解决问题。not broken, don't fix.

应⽤调优

应⽤调优-⼯具篇

  • ⼯具旨在帮助我们快速找到应⽤的性能瓶颈。
  • ⽇志分析⼯具⽐较与分析
  • ELK、GrayLog、SLSLog...
  • ELK搭建与使⽤
  • 现场演示
  • 调⽤链跟踪⼯具与对⽐
  • Skywalking、Sleuth + Zipkin、Jaeger...
  • Skywalking快速发现性能瓶颈

应⽤调优常⽤技巧-池化技术-对象池

通过复⽤对象,减少对象创建、垃圾回收的开销

适⽤场景

维护⼀些很⼤、创建很慢的对象,提升性能 缺点:有学习成本、增加了代码的复杂度

对象池框架

Apache Commons-Pool2
Commons-Pool2详解

两⼤类对象池:ObjectPool & KeyedObjectPool

ObjectPool

实现类如下,其中,最重要、功能最强、使⽤最⼴泛的GenericObjectPool,这个对象池⾮常的强⼤,它⽐较的通⽤,⽽且封装得也⾮常完备。

  • BaseObjectPool:抽象类,⽤来扩展⾃⼰的对象池
  • ErodingObjectPool:“腐蚀”对象池,代理⼀个对象池,并基于factor参数,为其添加“腐蚀”⾏为。归还的对象被腐蚀后,将会丢弃,⽽不是添加到空闲容量中。
  • GenericObjectPool:⼀个可配置的通⽤对象池实现。
  • ProxiedObjectPool:代理⼀个其他的对象池,并基于动态代理(⽀持JDK代理和CGLib代理),返回⼀个代理后的对象。该对象池主要⽤来增强对池化对象的控制,⽐如防⽌在归还该对象后,还继续使⽤该对象等。
  • SoftReferenceObjectPool:基于软引⽤的对象池
  • SynchronizedObjectPool:代理⼀个其他对象池,并为其提供线程安全的能⼒。
核⼼API如下
  • borrowObject() 从对象池中借对象
  • returnObject() 将对象归还到对象池
  • invalidateObject() 失效⼀个对象
  • addObject() 增加⼀个空闲对象,该⽅法适⽤于使⽤空闲对象预加载对象池
  • clear() 清空空闲的所有对象,并释放相关资源
  • close() 关闭对象池,并释放相关资源
  • getNumIdle() 获得空闲的对象数量
  • getNumActive() 获得被借出对象数量
KeyedObjectPool

这种对象池和ObjectPool的区别在于,它是通过key找对象的,从设计上来看和ObjectPool没什么区别。实现类如下,使⽤最⼴的是GenericKeyedObjectPool。

  • ErodingKeyedObjectPool 类似ErodingObjectPool
  • GenericKeyedObjectPool 类似GenericObjectPool
  • ProxiedKeyedObjectPool 类似ProxiedObjectPool
  • SynchronizedKeyedObjectPool 类似SynchronizedObjectPool
使⽤

scss

复制代码

new GenericObjectPool(PooledObjectFactory<T> factory)
new GenericObjectPool(PooledObjectFactory<T> factory, GenericObjectPoolConfig<T> config)
new GenericObjectPool(PooledObjectFactory<T> factory, GenericObjectPoolConfig<T> config, AbandonedConfig abando
nedConfig)

最重要的参数是PooledObjectFactory,⼀般来说,⼯⼚是需要我们⾃⼰根据业务需求去实现的。它是⽤来创建对象的,这其实就是设计模式⾥⾯的⼯⼚模式。

⽬前PooledObjectFactory有两个实现类。

  • BasePooledObjectFactory:抽象类,⽤于扩展⾃⼰的PooledObjectFactory
  • PoolUtils.SynchronizedPooledObjectFactory:内部类,代理⼀个其他的PooledObjectFactory,实现线程同步,⽤ PoolUtils.synchronizedPooledFactory() 创建

Factory核⼼⽅法:

  • makeObject 创建⼀个对象实例,并将其包装成⼀个PooledObject
  • destroyObject 销毁对象
  • validateObject 校验对象,确保对象池返回的对象是OK的
  • activateObject 重新初始化对象
  • passivateObject 取消初始化对象。GenericObjectPool的addIdleObject、returnObject、evict调⽤该⽅法。

Commons-Pool2总体分析

  • ObjectPool:对象池,最核⼼:GenericObjectPool、 GenericKeyedObjectPool。
  • Factory:创建&管理PooledObject,⼀般要⾃⼰扩展
  • PooledObject:包装原有的对象,从⽽让对象池管理,⼀般⽤DefaultPooledObject即可
Factory示例

java

复制代码

class MyPooledObjectFactory implements PooledObjectFactory<Model> { 
   public static final Logger LOGGER = LoggerFactory.getLogger(MyPooledObjectFactory.class); 
   @Override
   public PooledObject<Model> makeObject() throws Exception {
      DefaultPooledObject<Model> object = new DefaultPooledObject<>(new Model(1, "S")); 
      LOGGER.info("makeObject..state = {}", object.getState());
      return object; 
   }
  @Override
  public void destroyObject(PooledObject p) throws Exception{
      LOGGER.info("destroyObject..state = {}", object.getState());
   }
  @Override
  public boolean validateObject(PooledObject p) {
      LOGGER.info("validateObject..state = {}", object.getState());
      return true;
   }
  @Override
  public void activateObject(PooledObject p) throws Exception{
    LOGGER.info("activateObject..state = {}", p.getState());
   }
@Override
  public void passivateObject(PooledObject p) {
      LOGGER.info("passivateObject..state = {}", object.getState());
      return true;
   }

所有操作面向的都是PooledObject这个参数,makeObject返回的是PooledObject,其他API为什么操作的也是 PooledObject,⽽不是直接操作我们创建的对象呢?

这其实也是commons-pool设计巧妙之处。Pooledobject可以对原始对象进⾏包装,从⽽被对象池管理。⽬前 pooledobject有两个实现类:

  • DefaultPooledObject:包装原始对象,实现监控(例如创建时间、使⽤时间等)、状态跟踪等
  • PooledSoftReference:封装了DefaultPooledObject,⽤来和SoftReferenceObjectPool配合使⽤。
DefaultPooledObject定义了对象的若⼲种状态
  • IDLE 对象在队列中,并空闲。
  • ALLOCATED 使⽤中(即出借中)
  • EVICTION 对象当在队列中,正在进⾏驱逐测试
  • EVICTION_RETURN_TO_HEAD 对象驱逐测试通过后,放回到队列头部
  • VALIDATION 对象当前在队列中,空闲校验中
  • VALIDATION_PREALLOCATED 对象当前不在队列中,出借前校验中 VALIDATION_RETURN_TO_HEAD 对象当前不在队列中,校验通过后放回头部 INVALID 对象失效,驱逐测试失败、校验失败、对象销毁,都会将对象置为 INVALID。
  • ABANDONED 放逐中,如果对象上次使⽤时间超过removeAbandonedTimeout的配置,则将其标记为ABANDONED。标记为ABANDONED的对象即将变成 INVALID。
  • RETURNING 对象归还池中。

JVM调优

本系列专题将针对于Oracle Java HotSpot虚拟机为为开发者们提供不同的Java Heap内存空间的较为深入的分析介绍。对于任何接触的开发者都是非常重要的理论依据。频繁遇到的内存问题,提供生产环境的优化调整。那么适当的实战层级的Java虚拟机的内存空间分析能力是至关重要的。

前提概述

  • Java虚拟机是你的Java程序运行的基础,它为你提供动态的分配内存服务、垃圾收集、线程调度和切换、IO处理和本机操作等
  • Java堆空间是运行时Java程序的内存“容器”,它提供给您的Java应用程序所需的适当内存空间(Java堆、本机堆),并由JVM本身去管理。

JVM HotSpot内存被划分2类和5空间:

  • Heap堆内存空间:属于线程共享区域,也是我们JVM的内存管理范畴的最大的一部分运行时内存区域。
  • 方法区(永久代/元空间):属于线程共享区域,往往我们会忽略了这个区域的内存回收能力。
  • 本地堆 (C-Heap):本地方法的调用栈。
  • 虚拟机栈:Java方法的调用栈。


Heap堆内存空间

JVM的堆空间的变化在<18的版本之内,主要有一个分水岭,主要集中在8之前和8之后。

JDK8之前的对空间

JDK8之前的Heap空间如下图所示:


JDK8之后的Heap空间如下图所示:


主要时针对于方法区的实现机制:永久代 -> 元空间结构模型,接下来我们看看元数据空间在方法区中的分布结构模型。

后续版本中的-元空间和方法去的内存才能出分配关系


可以看到JDK8之后,方法去的实现有元空间和一部分堆内存组成。之前主要只有单纯的永久代去实现的。

常量池

常量池主要有静态常量池和运行时常量池组成。

  • 类信息
  • 类的版本
  • 字段描述信息
  • 方法描述信息
  • 接口和父类等描述信息
  • class文件常量池(静态常量池)
静态常量池,也叫class⽂件常量池,主要存放:
  • 字⾯量:例如⽂本字符串、 final修饰的常量。
  • 符号引⽤:例如类和接⼝的全限定名、字段的名称和描述符、⽅法的名称和描述符。
运⾏时常量池

当类加载到内存中后,JVM就会将静态常量池中的内容存放到运⾏时的常量池中;运⾏时常量池⾥⾯存储的主要是编译期间⽣成的字⾯量、符号引⽤等等。如下图对应的字符串常量在字符串常量池中的存储模式。



字符串常量池

字符串常量池,也可以理解成运⾏时常量池分出来的⼀部分,类加载到内存的时候,字符串,会存到字符串常量池⾥⾯。



对象和类在内存分布

针对于代码的执行和存储在JVM的分布,主要集中在栈空间和堆空间、方法区。它们各个的职能不同,对应的能力也是不同的。我们针对于一段代码块进行分析和介绍

虚拟机栈的基本结构模型



代码在堆栈中的存储结构信息



内存泄漏怎么排查[java内存溢出排查]

top 等查看系统内存概况

top:显示所有进程运行情况,按M键按照内存大小排序。

使用格式

css

复制代码

top [-] [d] [p] [q] [c] [C] [S] [s] [n]

参数说明

  • d:指定每两次屏幕信息刷新之间的时间间隔,当然用户可以使用s交互命令来改变之
  • p:通过指定监控进程ID来仅仅监控某个进程的状态。
  • q:该选项将使top没有任何延迟的进行刷新。如果调用程序有超级用户权限,那么top将以尽可能高的优先级运行。
  • S:指定累计模式。
  • s:使top命令在安全模式中运行。这将去除交互命令所带来的潜在危险。
  • i:使top不显示任何闲置或者僵死进程。
  • c:显示整个命令行而不只是显示命令名。

命令说明

  • jmx 快速发现jvm中的内存异常项

【实战阶段】JVM排查问题优化参数

css

复制代码

jps [-q] [-mlvV] [<hostid>]

参数如下:

  • -q 只显示进程号
  • -m 显示传递给main⽅法的参数
  • -l 显示应⽤main class的完整包名应⽤的jar⽂件完整路径名
  • -v 显示传递给JVM的参数
  • -V 禁⽌输出类名、JAR⽂件名和传递给main⽅法的参数,仅显示本地JVM标识符的列表

hostid的参数格式

  • hostid:想要查看的主机的标识符,格式为: [protocol:][[//]hostname][:port][/servername] ,其中:
  • protocol:通信协议,默认rmi
  • hostname:⽬标主机的主机名或IP地址
  • port:通信端⼝,对于默认 rmi 协议,该参数⽤来指定 rmiregistry 远程主机上的端⼝号。如省略该参数,并且该
  • protocol指示rmi,则使⽤默认使⽤1099端⼝。
  • servicename:服务名称,取值取决于实现⽅式,对于rmi协议,此参数代表远程主机上RMI远程对象的名称

今天就写到这里,未完待续,等待下一部分的内容。

目录
打赏
0
0
0
0
379
分享
相关文章
|
1月前
|
【Java并发】【线程池】带你从0-1入门线程池
欢迎来到我的技术博客!我是一名热爱编程的开发者,梦想是编写高端CRUD应用。2025年我正在沉淀中,博客更新速度加快,期待与你一起成长。 线程池是一种复用线程资源的机制,通过预先创建一定数量的线程并管理其生命周期,避免频繁创建/销毁线程带来的性能开销。它解决了线程创建成本高、资源耗尽风险、响应速度慢和任务执行缺乏管理等问题。
172 60
【Java并发】【线程池】带你从0-1入门线程池
|
19天前
|
【Java并发】【synchronized】适合初学者体质入门的synchronized
欢迎来到我的Java线程同步入门指南!我不是外包员工,梦想是写高端CRUD。2025年我正在沉淀中,博客更新速度加快,欢迎点赞、收藏、关注。 本文介绍Java中的`synchronized`关键字,适合初学者。`synchronized`用于确保多个线程访问共享资源时不会发生冲突,避免竞态条件、保证内存可见性、防止原子性破坏及协调多线程有序访问。
53 8
【Java并发】【synchronized】适合初学者体质入门的synchronized
|
20天前
|
《从头开始学java,一天一个知识点》之:数组入门:一维数组的定义与遍历
**你是否也经历过这些崩溃瞬间?** - 看了三天教程,连`i++`和`++i`的区别都说不清 - 面试时被追问&quot;`a==b`和`equals()`的区别&quot;,大脑突然空白 - 写出的代码总是莫名报NPE,却不知道问题出在哪个运算符 这个系列就是为你打造的Java「速效救心丸」!我们承诺:每天1分钟,地铁通勤、午休间隙即可完成学习;直击痛点,只讲高频考点和实际开发中的「坑位」;拒绝臃肿,没有冗长概念堆砌,每篇都有可运行的代码标本。明日预告:《多维数组与常见操作》。 通过实例讲解数组的核心认知、趣味场景应用、企业级开发规范及优化技巧,帮助你快速掌握Java数组的精髓。
57 23
【Java并发】【AQS】适合初学者体质的AQS入门
AQS这是灰常重要的哈,很多JUC下的框架的核心,那都是我们的AQS,所以这里,我们直接开始先研究AQS。 那说到研究AQS,那我们应该,使用开始说起🤓 入门 什么是AQS? AQS(Abst
33 8
智慧产科一体化管理平台源码,基于Java,Vue,ElementUI技术开发,二开快捷
智慧产科一体化管理平台覆盖从备孕到产后42天的全流程管理,构建科室协同、医患沟通及智能设备互联平台。通过移动端扫码建卡、自助报道、智能采集数据等手段优化就诊流程,提升孕妇就诊体验,并实现高危孕产妇五色管理和孕妇学校三位一体化管理,全面提升妇幼健康宣教质量。
58 12
Java中的字符集编码入门-增补字符(转载)
本文探讨Java对Unicode的支持及其发展历程。文章详细解析了Unicode字符集的结构,包括基本多语言面(BMP)和增补字符的表示方法,以及UTF-16编码中surrogate pair的使用。同时介绍了代码点和代码单元的概念,并解释了UTF-8的编码规则及其兼容性。
122 60
SaaS云计算技术的智慧工地源码,基于Java+Spring Cloud框架开发
智慧工地源码基于微服务+Java+Spring Cloud +UniApp +MySql架构,利用传感器、监控摄像头、AI、大数据等技术,实现施工现场的实时监测、数据分析与智能决策。平台涵盖人员、车辆、视频监控、施工质量、设备、环境和能耗管理七大维度,提供可视化管理、智能化报警、移动智能办公及分布计算存储等功能,全面提升工地的安全性、效率和质量。
CRaC技术助力ACS上的Java应用启动加速
容器计算服务借助ACS的柔性算力特性并搭配CRaC技术极致地提升Java类应用的启动速度。
Java高级应用开发:基于AI的微服务架构优化与性能调优
在现代企业级应用开发中,微服务架构虽带来灵活性和可扩展性,但也增加了系统复杂性和性能瓶颈。本文探讨如何利用AI技术,特别是像DeepSeek这样的智能工具,优化Java微服务架构。AI通过智能分析系统运行数据,自动识别并解决性能瓶颈,优化服务拆分、通信方式及资源管理,实现高效性能调优,助力开发者设计更合理的微服务架构,迎接未来智能化开发的新时代。
Java最新图形化界面开发技术——JavaFx教程(含UI控件用法介绍、属性绑定、事件监听、FXML)
JavaFX是Java的下一代图形用户界面工具包。JavaFX是一组图形和媒体API,我们可以用它们来创建和部署富客户端应用程序。 JavaFX允许开发人员快速构建丰富的跨平台应用程序,允许开发人员在单个编程接口中组合图形,动画和UI控件。本文详细介绍了JavaFx的常见用法,相信读完本教程你一定有所收获!
3021 2
Java最新图形化界面开发技术——JavaFx教程(含UI控件用法介绍、属性绑定、事件监听、FXML)
AI助理

你好,我是AI助理

可以解答问题、推荐解决方案等