深入理解Java源码:提升技术功底,深度掌握技术框架,快速定位线上问题
为什么要看源码:
1、提升技术功底: 学习源码里的优秀设计思想,比如一些疑难问题的解决思路,还有一些优秀的设计模式,整体提升自己的技术功底
2、深度掌握技术框架: 源码看多了,对于一个新技术或框架的掌握速度会有大幅提升,看下框架demo 大致就能知道底层的实现,技术框 架更新再快也不怕
3、快速定位线上问题: 遇到线上问题,特别是框架源码里的问题(比如bug), 能够快速定位,这就是相比其他没看过源码的人的优势 4、对面试大有裨益: 面试一线互联网公司对于框架技术一般都会问到源码级别的实现
5、知其然知其所以然: 对技术有追求的人必做之事,使用了一个好的框架,很想知道底层是如何实现的
6、拥抱开源社区: 参与到开源项目的研发,结识更多大牛,积累更多优质人脉
看源码方法:
1、先使用: 先看官方文档快速掌握框架的基本使用
2、抓主线:找一个demo 入手,顺藤摸瓜快速静态看一遍框架的主线源码(抓大放小),画出源码主流程图,切勿一开始就陷入源码的细枝 末节,否则会把自己绕晕
3、画图做笔记: 总结框架的一些核心功能点,从这些功能点入手深入到源码的细节,边看源码边画源码走向图,并对关键源码的理解做 笔记,把源码里的闪光点都记录下来,后续借鉴到工作项目中,理解能力强的可以直接看静态源码,也可以边看源码边debug 源码执行过 程,观察一些关键变量的值
4、整合总结:所有功能点的源码都分析完后,回到主流程图再梳理一遍,争取把自己画的所有图都在脑袋里做一个整合


Netty 高并发高性能架构设计精髓
· 主从Reactor线程模型
· NIO 多路复用非阻塞
· 无锁串行化设计思想
· 支持高性能序列化协议
● 零拷贝(直接内存的使用) · ByteBuf内存池设计
· 灵活的TCP 参数配置能力 · 并发优化
无锁串行化设计思想
在大多数场景下,并行多线程处理可以提升系统的并发性能。但是,如果对于共享资源的并发访问处理不当,会带来严重的锁竞争,这最 终会导致性能的下降。为了尽可能的避免锁竞争带来的性能损耗,可以通过串行化设计,即消息的处理尽可能在同一个线程内完成,期间 不进行线程切换,这样就避免了多线程竞争和同步锁。NIO 的多路复用就是一种无锁串行化的设计思想(理解下Redis和Netty的线程模型) 为了尽可能提升性能, Netty采用了串行无锁化设计,在IO线程内部进行串行操作,避免多线程竞争导致的性能下降。表面上看,串行化 设计似乎CPU 利用率不高,并发程度不够。但是,通过调整NIO 线程池的线程参数,可以同时启动多个串行化的线程并行运行,这种局部无锁化的串行线程设计相比一个队列-多个工作线程模型性能更优。
Netty的NioEventLoop 读取到消息之后,直接调用ChannelPipeline的fireChannelRead (Object msg),只要用户不主动切换线程, 一直 会由NioEventLoop调用到用户的Handler, 期间不进行线程切换,这种串行化处理方式避免了多线程操作导致的锁的竞争,从性能角度 看是最优的。
直接内存
直接内存 (Direct Memory) 并不是虚拟机运行时数据区的一部分,也不是Java虚拟机规范中定义的内存区域,某些情况下这部分内存也 会被频繁地使用,而且也可能导致OutOfMemoryError 异常出现。Java里用DirectByteBuffer可以分配一块直接内存(堆外内存),元空间 对应的内存也叫作直接内存,它们对应的都是机器的物理内存。

直接内存分配源码分析:
public static ByteBuffer allocateDirect(int capacity) {return new DirectByteBuffer(capacity);
}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.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)) {address = base + ps - (base & (ps - 1));} else {address = base;}cleaner = Cleaner.create(this, new Deallocator(base, size, cap));att = null;
}public native long allocateMemory(long bytes);UNSAFE_ENTRY(jlong,Unsafe_AllocateMemory(JNIEnv *env,jobject unsafe,jlong size)) {Unsafewrapper("Unsafe_AllocateMemory");size_t sz = (size_t)size;if(sz != (julong)size || size < 0){THROW_0(vmSymbols::java_lang_IllegalArgumentException());}sz = round_to(sz,HeapWordSize);void *x = os::malloc(sz,mtInternal);if(x == NULL){THROW_0(vmSymbols::java_lang_OutofMemoryError());}return addr to java(x);
}
使用直接内存的优缺点:
优点:
· 不占用堆内存空间,减少了发生GC的可能
· java 虚拟机实现上,本地IO 会直接操作直接内存(直接内存=>系统调用=>硬盘/网卡),而非直接内存则需要二次拷贝(堆内 存=>直接内存=>系统调用=>硬盘/网卡)
缺点:
● 初始分配较慢
· 没有JVM直接帮助管理内存,容易发生内存溢出。为了避免一直没有FULLGC, 最终导致直接内存把物理内存耗完。我们可以 指定直接内存的最大值,通过-XX:MaxDirectMemorySize 来指定,当达到阈值的时候,调用system.gc来进行一次FULL GC,间 接把那些没有被使用的直接内存回收掉。


ByteBuf内存池设计
随着JVM虚拟机和JIT即时编译技术的发展,对象的分配和回收是个非常轻量级的工作。但是对于缓冲区Buffer(相当于一个内存块),情况 却稍有不同,特别是对于堆外直接内存的分配和回收,是一件耗时的操作。为了尽量重用缓冲区,Netty提供了基于ByteBuf内存池的缓冲 区重用机制。需要的时候直接从池子里获取ByteBuf使用即可,使用完毕之后就重新放回到池子里去。下面我们一起看下Netty ByteBuf的实现:

可以看下netty的读写源码里面用到的ByteBuf内存池,比如read源码NioByteUnsafe.read();


继续看newDirectBuffer方法,我们发现它是一个抽象方法,由AbstractByteBufAllocator的子类负责具体实现,代码如下:

代码跳转到PooledByteBufAllocator的newDirectBuffer方法,从Cache中获取内存区域PoolArena,调用它的allocate方法进行内存分配:

PoolArena的allocate方法如下:

灵活的TCP参数配置能力
合理设置TCP参数在某些场景下对于性能的提升可以起到显著的效果,例如接收缓冲区SO_RCVBUF和发送缓冲区SO_SNDBUF。如果设置不当,对性能的影响是非常大的。通常建议值为128K或者256K。
Netty在启动辅助类ChannelOption中可以灵活的配置TCP参数,满足不同的用户场景。

并发优化
volatile的大量、正确使用;
CAS和原子类的广泛使用;
线程安全容器的使用;
通过读写锁提升并发性能。
ByteBuf扩容机制
如果我们需要了解ByteBuf的扩容,我们需要先了解ByteBuf中定义的几个成员变量,再从源码的角度来分析扩容。


总结:Netty的ByteBuf需要动态扩容来满足需要,扩容过程: 默认门限阈值为4MB(这个阈值是一个经验值,不同场景,可能取 值不同),当需要的容量等于门限阈值,使用阈值作为新的缓存区容量 目标容量,如果大于阈值,采用每次步进4MB的方式进行 内存扩张((需要扩容/4MB)*4MB),扩张后需要和最大内存(maxCapacity)进行比较,大于maxCapacity的话就用 maxCapacity,否则使用扩容值 目标容量,如果小于阈值,采用倍增的方式,以64(字节)作为基本数值,每次翻倍增长64 -->128 --> 256,直到倍增后的结果大于或等于需要的容量值。
补充:handler的生命周期回调接口调用顺序
/*** 在channel的pipeline里如下handler:ch.pipeline().addLast(new LifeCycleInBoundHandler());* handler 的生命周期回调接口调用顺序:* handlerAdded -> channelRegistered -> channelActive -> channelRead -> channelReadComplete -> channelInactive -> channelUnregistered -> handlerRemoved** handlerAdded: 新建立的连接会按照初始化策略,把handler添加到该channel的pipeline里面,也就是channel.pipeline.addLast(new LifeCycleInBoundHandler)执行完成后的回调;* channelRegistered: 当该连接分配到具体的worker线程后,该回调会被调用。* channelActive: channel的准备工作已经完成,所有的pipeline添加完成,并分配到具体的线上上,说明该channel准备就绪,可以使用了。* channelRead: 客户端向服务端发来数据,每次都会回调此方法,表示有数据可读;* channelReadComplete: 服务端每次读完一次完整的数据之后,回调该方法,表示数据读取完毕;* channelInactive: 当连接断开时,该回调会被调用,说明这时候底层的TCP连接已经被断开了。* channelUnRegistered: 对应channelRegistered, 当连接关闭后,释放绑定的worker线程;* handlerRemoved: 对应handlerAdded, 将handler从该channel的pipeline移除后的回调方法。*/
public class LifecycleInBoundHandler extends ChannelInboundHandlerAdapter {@Overridepublic void channelRegistered(ChannelHandlerContext ctx) throws Exception {System.out.println("channelRegistered: channel注册到NioEventLoop");super.channelRegistered(ctx);}@Overridepublic void channelUnregistered(ChannelHandlerContext ctx) throws Exception {System.out.println("channelUnregistered: channel取消和NioEventLoop的绑定");super.channelUnregistered(ctx);}@Overridepublic void channelActive(ChannelHandlerContext ctx) throws Exception {System.out.println("channelActive: channel准备就绪");super.channelActive(ctx);}@Overridepublic void channelInactive(ChannelHandlerContext ctx) throws Exception {System.out.println("channelInactive: channel被关闭");super.channelInactive(ctx);}@Overridepublic void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {System.out.println("channelRead: channel中有可读的数据");super.channelRead(ctx, msg);}@Overridepublic void channelReadComplete(ChannelHandlerContext ctx) throws Exception {System.out.println("channelReadComplete: channel读数据完成");super.channelReadComplete(ctx);}@Overridepublic void handlerAdded(ChannelHandlerContext ctx) throws Exception {System.out.println("handlerAdded: handler被添加到channel的pipeline");super.handlerAdded(ctx);}@Overridepublic void handlerRemoved(ChannelHandlerContext ctx) throws Exception {System.out.println("handlerRemoved: handler从channel的pipeline中移除");super.handlerRemoved(ctx);}
}
相关文章:
深入理解Java源码:提升技术功底,深度掌握技术框架,快速定位线上问题
为什么要看源码: 1、提升技术功底: 学习源码里的优秀设计思想,比如一些疑难问题的解决思路,还有一些优秀的设计模式,整体提升自己的技术功底 2、深度掌握技术框架: 源码看多了,对于一个新技术…...
寒假前端第一次作业
1、用户注册: <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>用户注册</title> …...
【LabVIEW FPGA入门】创建第一个LabVIEW FPGA程序
本教程仅以compactRIO(FPGA-RT)举例 1.系统配置 1.1软件安装 FPGA-RT 1. LabVIEW Development System (Full or Professional) 2. LabVIEW Real-Time Module 3. LabVIEW FPGA Module 4. NI-RIO drivers 1.2硬件配置 1.使用线缆连接CompactRIO至主机…...
【STM32】STM32学习笔记-USART串口数据包(28)
00. 目录 文章目录 00. 目录01. 串口简介02. HEX数据包03. 文本数据包04. HEX数据包接收05. 文本数据包接收06. 预留07. 附录 01. 串口简介 串口通讯(Serial Communication)是一种设备间非常常用的串行通讯方式,因为它简单便捷,因此大部分电子设备都支持…...
Java网络爬虫--HttpClient
目录标题 技术介绍有什么优点?怎么在项目中引入? 请求URLEntityUtils 类GET请求带参数的GET请求POST请求 总结 技术介绍 HttpClient 是 Apache Jakarta Common 下的子项目,用来提供高效的、功能丰富的、支持 HTTP 协议的客户端编程工具包。相…...
若依项目的table列表中对每一个字段增加排序按钮(单体版和前后端分离版)
一、目标:每一个字段都添加上下箭头用来排序 只需要更改前端代码,不需要更改后端代码,后面会讲解原理 二、单体版实现方式: 1.在options中添加sortable:true 2.在需要排序的字段中添加sortable:true 三、前后端分离版 1.el-table上添加@sort-change=“handleSortChange”…...
Linux自动化部署脚本
1:最近项目部署比较频繁终于熬不住了 就有下面的这东西 #!/bin/sh #报错停止运行 set -e # 获取tomcat的PID TOMCAT_PID$(ps -ef | grep tomcat | grep -v grep | awk {print $2}) # tomcat的启动文件位置 START_TOMCAT/mnt/tomcat/bin/startup.sh # 项目文件部署位置 PROJECT…...
lvgl修改图片大小上限
在lvgl中读取图片文件时,被读取的图片具有上限,也就是2048像素。这会造成两个非预期的结果: 超过2048像素的部分会被裁去。表示图片的结构体lv_img_t中的w和h变量值是图片像素被2048求余。例如,当一个图片高为2048像素时…...
阻止持久性攻击改善网络安全
MITRE ATT&CK框架是一个全球可访问的精选知识数据库,其中包含基于真实世界观察的已知网络攻击技术和策略。持久性是攻击者用来访问系统的众多网络攻击技术之一;在获得初始访问权限后,他们继续在很长一段时间内保持立足点,以窃取数据、修改…...
MFC与Qt多个控件响应统一响应消息处理
就目前使用C开发框架来说,今天来讲述下MFC框架下与Qt框架下,如何让多个控件响应统一消息处理方法。 功能:假设有5个按钮,需要响应同一个处理函数,该如何实现呢? Qt方式 开发环境:win10 VS201…...
Camunda rest api鉴权
对于rest api 不能没有限制的任何人随意调用,需要提供账号信息。 一:工作流引擎增加过滤器 /*** 对/engine-rest/*进行鉴权,防止非法攻击* 客户端调用需要配置用户凭证否则报错401* camunda.bpm.client.basic-auth.username* camunda.bpm.cl…...
【PostgreSQL】在DBeaver中实现序列、函数、视图、触发器设计
【PostgreSQL】在DBeaver中实现序列、函数、触发器、视图设计 基本配置一、序列1.1、序列使用1.1.1、设置字段为主键,数据类型默认整型1.1.2、自定义序列,数据类型自定义 1.2、序列延申1.2.1、理论1.2.2、测试1.2.3、小结 二、函数2.1、SQL直接创建2.1.1…...
PyQt5-小总结
之前学习PyQt5,然后那段时间想做一个桌面小程序,后来由于学习内容较多就做了一小部分,但是可以进行页面跳转。大家如果是初学者对Python感兴趣而且刚学数据库时可以看看代码,可能会有点启发。 效果: 登录进来是这&…...
vue父组件给子组件传值,子组件不渲染的原因及解决方法
父组件传递给子组件的数据,如果是一个复杂对象(例如一个数组或对象),那么子组件只会监听对象的引用而不是对象的内容。这意味着当对象的内容发生变化时,子组件不会更新。 解决: 1、在子组件使用 watch 监听…...
【数据库】MySQL锁
一、锁的基本概念 1、锁的定义 锁是协调多个进程或线程并发访问数据库资源的一种机制。 MySQL中的锁是在服务器层或者存储引擎层实现的,保证了数据访问的一致性与有效性。但加锁是消耗资源的,锁的各种操作,包括获得锁、检测锁是否已解除、…...
mongodb学习篇
目录 前言基本概念数据库-database集合-collection文档-document 部署mongodblinux安装mongodbdocker安装mongodb MongoDB Shell (mongosh)命令行工具mongodb可视化-mongodb-compass、mongo-expressmongodb配置文件mongodb库、集合、文档库基本操作集合基本操作文档的增删改查C…...
kubernetes存储类迁移-备份恢复
背景介绍 kubernetes集群最开始使用了nfs作为存储,随着后续使用过程中数据量逐渐增加,nfs存储性能逐步出现不足,现增加了基于csi的分布式块存储后,需要对原有基于nfs存储类下的pv迁移到新的存储类下。 测试环境 k8s集群版本&am…...
python智能手机芯片
在未来,python智能手机芯片的发展方向可能包括以下几个方面: 强化处理能力:随着智能手机功能的不断扩展和用户需求的增加,处理器的性能需求也在不断提升。未来的python智能手机芯片可能会加强处理器的核心数量和频率,以…...
混淆技术概论
混淆技术概论 引言 在逆向工程领域,混淆技术是一种非常重要的技术手段,通过打破人们的思维惯性,使得逆向分析变得更加困难。本文将会介绍混淆技术的概念、分类及其应用,以及如何使用IPA Guard进行iOS IPA重签名。 混淆技术概述…...
pytest安装失败,报错Could not find a version that satisfies the requirement pytest
问题 安装pytest失败,尝试使用的命令有 pip install pytest pip3 install pytest pip install -U pytest pip install pytest -i https://pypi.tuna.tsinghua.edu.cn/simple但是都会报同样的错: 解决方案 发现可能是挂了梯子的原因,关掉…...
【Axure高保真原型】引导弹窗
今天和大家中分享引导弹窗的原型模板,载入页面后,会显示引导弹窗,适用于引导用户使用页面,点击完成后,会显示下一个引导弹窗,直至最后一个引导弹窗完成后进入首页。具体效果可以点击下方视频观看或打开下方…...
Linux应用开发之网络套接字编程(实例篇)
服务端与客户端单连接 服务端代码 #include <sys/socket.h> #include <sys/types.h> #include <netinet/in.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <arpa/inet.h> #include <pthread.h> …...
华为云AI开发平台ModelArts
华为云ModelArts:重塑AI开发流程的“智能引擎”与“创新加速器”! 在人工智能浪潮席卷全球的2025年,企业拥抱AI的意愿空前高涨,但技术门槛高、流程复杂、资源投入巨大的现实,却让许多创新构想止步于实验室。数据科学家…...
盘古信息PCB行业解决方案:以全域场景重构,激活智造新未来
一、破局:PCB行业的时代之问 在数字经济蓬勃发展的浪潮中,PCB(印制电路板)作为 “电子产品之母”,其重要性愈发凸显。随着 5G、人工智能等新兴技术的加速渗透,PCB行业面临着前所未有的挑战与机遇。产品迭代…...
day52 ResNet18 CBAM
在深度学习的旅程中,我们不断探索如何提升模型的性能。今天,我将分享我在 ResNet18 模型中插入 CBAM(Convolutional Block Attention Module)模块,并采用分阶段微调策略的实践过程。通过这个过程,我不仅提升…...
遍历 Map 类型集合的方法汇总
1 方法一 先用方法 keySet() 获取集合中的所有键。再通过 gey(key) 方法用对应键获取值 import java.util.HashMap; import java.util.Set;public class Test {public static void main(String[] args) {HashMap hashMap new HashMap();hashMap.put("语文",99);has…...
Objective-C常用命名规范总结
【OC】常用命名规范总结 文章目录 【OC】常用命名规范总结1.类名(Class Name)2.协议名(Protocol Name)3.方法名(Method Name)4.属性名(Property Name)5.局部变量/实例变量(Local / Instance Variables&…...
电脑插入多块移动硬盘后经常出现卡顿和蓝屏
当电脑在插入多块移动硬盘后频繁出现卡顿和蓝屏问题时,可能涉及硬件资源冲突、驱动兼容性、供电不足或系统设置等多方面原因。以下是逐步排查和解决方案: 1. 检查电源供电问题 问题原因:多块移动硬盘同时运行可能导致USB接口供电不足&#x…...
搭建DNS域名解析服务器(正向解析资源文件)
正向解析资源文件 1)准备工作 服务端及客户端都关闭安全软件 [rootlocalhost ~]# systemctl stop firewalld [rootlocalhost ~]# setenforce 0 2)服务端安装软件:bind 1.配置yum源 [rootlocalhost ~]# cat /etc/yum.repos.d/base.repo [Base…...
【JavaSE】多线程基础学习笔记
多线程基础 -线程相关概念 程序(Program) 是为完成特定任务、用某种语言编写的一组指令的集合简单的说:就是我们写的代码 进程 进程是指运行中的程序,比如我们使用QQ,就启动了一个进程,操作系统就会为该进程分配内存…...
