深入理解Java源码:提升技术功底,深度掌握技术框架,快速定位线上问题

为什么要看源码:

1、提升技术功底: 学习源码里的优秀设计思想,比如一些疑难问题的解决思路,还有一些优秀的设计模式,整体提升自己的技术功底

2深度掌握技术框架: 源码看多了,对于一个新技术或框架的掌握速度会有大幅提升,看下框架demo 大致就能知道底层的实现,技术框 架更新再快也不怕

3快速定位线上问题: 遇到线上问题,特别是框架源码里的问题(比如bug), 能够快速定位,这就是相比其他没看过源码的人的优势 4对面试大有裨益: 面试一线互联网公司对于框架技术一般都会问到源码级别的实现

5知其然知其所以然: 对技术有追求的人必做之事,使用了一个好的框架,很想知道底层是如何实现的

6拥抱开源社区: 参与到开源项目的研发,结识更多大牛,积累更多优质人脉

看源码方法:

1、先使用: 先看官方文档快速掌握框架的基本使用

2抓主线:找一个demo 入手,顺藤摸瓜快速静态看一遍框架的主线源码(抓大放小),画出源码主流程图,切勿一开始就陷入源码的细枝 末节,否则会把自己绕晕

3画图做笔记: 总结框架的一些核心功能点,从这些功能点入手深入到源码的细节,边看源码边画源码走向图,并对关键源码的理解做  笔记,把源码里的闪光点都记录下来,后续借鉴到工作项目中,理解能力强的可以直接看静态源码,也可以边看源码边debug  源码执行过 程,观察一些关键变量的值

4整合总结:所有功能点的源码都分析完后,回到主流程图再梳理一遍,争取把自己画的所有图都在脑袋里做一个整合

Netty   高并发高性能架构设计精髓

·  主从Reactor线程模型

·   NIO 多路复用非阻塞

·  无锁串行化设计思想

·  支持高性能序列化协议

●   零拷贝(直接内存的使用) ·   ByteBuf内存池设计

·  灵活的TCP 参数配置能力 ·   并发优化

无锁串行化设计思想

        在大多数场景下,并行多线程处理可以提升系统的并发性能。但是,如果对于共享资源的并发访问处理不当,会带来严重的锁竞争,这最 终会导致性能的下降。为了尽可能的避免锁竞争带来的性能损耗,可以通过串行化设计,即消息的处理尽可能在同一个线程内完成,期间 不进行线程切换,这样就避免了多线程竞争和同步锁。NIO 的多路复用就是一种无锁串行化的设计思想(理解下RedisNetty的线程模型) 为了尽可能提升性能, Netty采用了串行无锁化设计,在IO线程内部进行串行操作,避免多线程竞争导致的性能下降。表面上看,串行化 设计似乎CPU 利用率不高,并发程度不够。但是,通过调整NIO 线程池的线程参数,可以同时启动多个串行化的线程并行运行,这种局部无锁化的串行线程设计相比一个队列-多个工作线程模型性能更优。

        Netty的NioEventLoop 读取到消息之后,直接调用ChannelPipelinefireChannelRead (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);}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/606577.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

猫头虎分享已解决Bug || Error: ImagePullBackOff (K8s)

博主猫头虎的技术世界 &#x1f31f; 欢迎来到猫头虎的博客 — 探索技术的无限可能&#xff01; 专栏链接&#xff1a; &#x1f517; 精选专栏&#xff1a; 《面试题大全》 — 面试准备的宝典&#xff01;《IDEA开发秘籍》 — 提升你的IDEA技能&#xff01;《100天精通Golang》…

快速幂算法总结

知识概览 快速幂可以在O(logk)的时间复杂度之内求出来的结果。 例题展示 快速幂 题目链接 活动 - AcWing 系统讲解常用算法与数据结构&#xff0c;给出相应代码模板&#xff0c;并会布置、讲解相应的基础算法题目。https://www.acwing.com/problem/content/877/ 代码 #inc…

电子学会2023年12月青少年软件编程(图形化)等级考试试卷(一级)真题,含答案解析

青少年软件编程(图形化)等级考试试卷(一级) 分数:100 题数:37 一、单选题(共25题,共50分) 1. 观察下列每个圆形中的四个数,找出规律,在括号里填上适当的数?( )

Python武器库开发-武器库篇之端口扫描器开发(四十四)

Python武器库开发-武器库篇之端口扫描器开发(四十四) 端口是计算机网络中用于区分不同应用程序或服务的逻辑概念。计算机通过端口号来识别数据包应该发送给哪个应用程序或服务。在互联网协议&#xff08;IP&#xff09;中&#xff0c;端口号是16位的数字&#xff0c;范围从0到…

计算机毕业设计 基于javaweb的学生交流培养管理平台/系统的设计与实现 Java实战项目 附源码+文档+视频讲解

博主介绍&#xff1a;✌从事软件开发10年之余&#xff0c;专注于Java技术领域、Python人工智能及数据挖掘、小程序项目开发和Android项目开发等。CSDN、掘金、华为云、InfoQ、阿里云等平台优质作者✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精…

软件测试|测试平台开发-Flask 入门:URL组成部分详解

简介 Flask 是一款流行的 Python Web 框架&#xff0c;它简单轻量而灵活&#xff0c;适用于构建各种规模的 Web 应用程序。在 Flask 中&#xff0c;URL&#xff08;Uniform Resource Locator&#xff09;是指定 Web 应用程序中资源的唯一标识符。URL 组成部分是构成一个完整 U…

IDEA卡在”正在解析Maven依赖项“的解决方法

修改Maven镜像&#xff0c;修改Maven镜像为阿里云镜像 1.文件路径 C:\Program Files\JetBrains\IntelliJ IDEA 2022.2.3\plugins 2.因为我使用的是InteliJ idea 自带的Maven&#xff0c;所以直接去idea 插件安装目录&#xff0c;找到Maven插件&#xff0c;找到setting.xml 文…

数据库系统-甘晴void学习笔记

数据库系统笔记 计科210X 甘晴void 202108010XXX 教材&#xff1a;《数据库系统概论》第6版 &#xff08;图片来源于网络&#xff0c;侵删&#xff09; 文章目录 数据库系统<br>笔记第一篇 基础篇1 绪论1.1数据库系统概述1.2数据模型1.3数据库系统的结构(三级模式结构…

【代码随想录算法训练营-第七天】【哈希表】454,383,15,18

454. 四数相加 II 第一遍 思路 想不出来&#xff0c;除了暴力解法&#xff0c;完全想不出来其他解法&#xff0c;看答案思路了…学习了两个新的方法&#xff1a; getOrDefault&#xff1a;返回指定键对应的值&#xff0c;如果不存在&#xff0c;则返回默认值containsKey&…

LiveGBS流媒体平台GB/T28181功能-国标级联对接海康大华宇视华为等上级平台选择通道支持只看已选只看未选

LiveGBS功能国标级联对接海康大华宇视华为等上级平台选择通道支持只看已选只看未选 1、国标级联2、只看已选3、只看未选4、搭建GB28181视频直播平台 1、国标级联 LiveGBS可以作为下级平台&#xff0c;级联到第三方国标平台&#xff0c;详见&#xff1a; LiveGBS国标GB/T28181流…

支持API文档生成,API管理工具:Apipost

随着数字化转型的加速&#xff0c;API&#xff08;应用程序接口&#xff09;已经成为企业间沟通和数据交换的关键。而在API开发和管理过程中&#xff0c;API文档、调试、Mock和测试的协作显得尤为重要。Apipost正是这样一款一体化协作平台&#xff0c;旨在解决这些问题&#xf…

Java网络爬虫--概述与原理

目录标题 基本概念与原理爬虫与搜索系统的关系爬虫运行原理爬虫步骤DNS域名解析 爬虫开发本质网络爬虫的分类通用网络爬虫聚集网络爬虫增量式网络爬虫Deep Web爬虫 参考文献 基本概念与原理 爬虫又叫网络蜘蛛&#xff0c;一种运行在互联网上用来获取数据的自动程序。 互联网的…

可狱可囚的爬虫系列课程 10:在网站中寻找 API 接口

上一篇文章我们讲述了爬虫中一个比较重要的知识点&#xff0c;如何从 API 接口中获取数据&#xff0c;本篇文章我们继续讲述&#xff0c;如何在网站中寻找 API 接口&#xff0c;我们以“今日头条”网站 https://www.toutiao.com/ 为例。 如上图所示&#xff0c;如果要获取页面…

洗地机哪种牌子好?智能洗地机排行

选择一款性能稳定、使用方便的洗地机&#xff0c;对于家庭清洁至关重要。近年来&#xff0c;随着懒人经济的兴起&#xff0c;智能家电不断涌现。特别是在家居清洁领域&#xff0c;人们追求更加轻松便捷的清洁体验。洗地机行业近年来迎来了快速增长&#xff0c;各大厂商竞相推出…

C#,数值计算,矩阵相乘的源代码与数据可视化

俺玩数学&#xff0c;你玩技巧&#xff0c;俺不如你&#xff0c;佩服佩服&#xff01; 一、矩阵乘法的基本概念 定义&#xff1a;矩阵 A ∈ Rmn 和B ∈ Rnp 的乘积为矩阵C∈ Rmp 。 限制&#xff1a;矩阵A的列数应该与矩阵B的行数相等。 算法&#xff1a;矩阵A的第一行每个元…

【Docker】部署mysql 和 tomcat

目录 部署MySQL 1.搜索镜像 2. 拉取镜像 部署Tomcat 1. 搜索镜像 2.拉取镜像 3.查看镜像 部署MySQL 1.搜索镜像 docker search mysql 2. 拉取镜像 通过mysql 镜像创建对应的容器&#xff0c;并设置端口映射&#xff0c;目录映射 创建mysql 的目录 docker run -id \ …

【mysql】—— 事务

目录 &#xff08;一&#xff09;前言 &#xff08;二&#xff09;事务的理解 1、为什么会出现事务 2、什么是事务&#xff1f; 3、事务的版本支持 4、事务提交方式 &#xff08;三&#xff09;事务常见操作方式 1、正常演示 - 证明事务的开始与回滚 2、非正常演示…

第一个Java网络爬虫程序

目录 前言第一个Java网络爬虫程序总结 前言 网络爬虫是一种获取互联网信息的技术&#xff0c;它可以模拟浏览器行为&#xff0c;访问网站并提取所需的数据。在这个小Demo中&#xff0c;我们使用Java语言结合HttpClient库实现了一个简单的爬虫程序&#xff0c;用于抓取汽车之家…

网络字节序与主机字节序

字节序区分 多字节的数值在内存中高低位的排列方式会影响所表示的数值处理方式和显示。字节序以字节为基本单位&#xff0c;表示不同字节的存储顺序。 从存储顺序上区分&#xff0c;可分为大端字节序和小端字节序。从处理上区分&#xff0c;可区分为网络字节序和主机字节序。…

Rapberry Pi 4 安装VxWorks笔记

Rapberry Pi 4 安装VxWorks笔记 本文章发表与我的github page&#xff1a; Rapberry Pi 4 安装VxWorks笔记 | Hi, I am watershade. Welcome to my pages. 在github page会有更好体验和更多文章。 一、概述 ROS2推荐的操作系统是ubuntu,众所周知&#xff0c;linux并不是实时…