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

news2024/9/19 23:19:55

为什么要看源码:

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 {
    @Override
    public void channelRegistered(ChannelHandlerContext ctx) throws Exception {
        System.out.println("channelRegistered: channel注册到NioEventLoop");
        super.channelRegistered(ctx);
    }

    @Override
    public void channelUnregistered(ChannelHandlerContext ctx) throws Exception {
        System.out.println("channelUnregistered: channel取消和NioEventLoop的绑定");
        super.channelUnregistered(ctx);
    }

    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        System.out.println("channelActive: channel准备就绪");
        super.channelActive(ctx);
    }

    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
        System.out.println("channelInactive: channel被关闭");
        super.channelInactive(ctx);
    }

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        System.out.println("channelRead: channel中有可读的数据");
        super.channelRead(ctx, msg);
    }

    @Override
    public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
        System.out.println("channelReadComplete: channel读数据完成");
        super.channelReadComplete(ctx);
    }

    @Override
    public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
        System.out.println("handlerAdded: handler被添加到channel的pipeline");
        super.handlerAdded(ctx);
    }

    @Override
    public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
        System.out.println("handlerRemoved: handler从channel的pipeline中移除");
        super.handlerRemoved(ctx);
    }
}

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

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

相关文章

线性代数 --- 为什么LU分解中的下三角矩阵L的主对角线上都是1?

为什么LU分解中的下三角矩阵L的主对角线上都是1? 笔者的一些话&#xff1a; 为什么LU分解中L矩阵的主对角线上都是1&#xff1f;因为最近一段时间在研究LU分解的编程实现&#xff0c;这个问题也就时不时的从我脑子里面冒出来。但大多时候都是一闪而过&#xff0c;没有太在意。…

IIC Master 设计实现

写个IIC的主机来玩一玩。 仅100M时钟输入SCL波形工整&#xff0c;任意两个上升沿之间均为整数倍周期&#xff0c;占空比50%发送数据时SDA严格对其到SCL低电平正中间尽可能少的状态机不浪费资源数据逻辑和时序逻辑分离 接口设计中&#xff0c;我的思路是将数据与时序分离开&am…

数据结构-测试5

一、判断题 1.二叉树只能用二叉链表表示&#xff08;F&#xff09; 二叉树的存储结构有两种&#xff0c;顺序存储结构和链式存储结构 2. 装填因子是散列表的一个重要参数&#xff0c;它反映散列表的装满程度。(T) 装填因子越小&#xff0c;发生冲突的可能性越小 3. 在任何情况…

损失函数 - Focal Loss

b站账号 : Enzo_Mi 知识星球 : Enzo AI学习小组 | 小白分会 欢迎加入我的知识星球,一起来学习吧 ~ Focal Loss 1、Focal Loss 提出的背景2、正负样本数量不均衡问题 的解决 : baseline3、难分类样本/易分类样本 数量不均衡问题 的解决 : Focal Loss3、类别加权 Focal L…

1.7数算PPT选择汇总,PTA选择汇总,计算后缀表达式,中缀转后缀、前缀、快速排序

PTA选择汇总 在第一个位置后插入&#xff0c;注意是在后面插入&#xff0c;而不是前面&#xff1b;要移动49&#xff0c;为50-I&#xff0c;第25个的话&#xff0c;移25个 如果是插在前面&#xff0c;就移动50&#xff0c;N-I1&#xff0c;注意是插在前面还是后面 删第一个&a…

今日实践 — 附加数据库/重定向失败如何解决?

WMS数据库与重定向 前言正文如何建立数据库连接&#xff1f;第一步&#xff1a;打开SSMS&#xff0c;右击数据库&#xff0c;点击附加第二步&#xff1a;点击添加第三步&#xff1a;找到自己的数据库文件&#xff0c;点击确定按钮第四步&#xff1a;若有多个数据库&#xff0c;…

Hyperledger Fabric 管理链码 peer lifecycle chaincode 指令使用

链上代码&#xff08;Chaincode&#xff09;简称链码&#xff0c;包括系统链码和用户链码。系统链码&#xff08;System Chaincode&#xff09;指的是 Fabric Peer 中负责系统配置、查询、背书、验证等平台功能的代码逻辑&#xff0c;运行在 Peer 进程内&#xff0c;将在第 14 …

如何查找native服务的接口实现

以Netd为例&#xff1a; 首先adb看一下服务的接口&#xff1a; 接口文件是INetd&#xff0c;去源码找一下INetd.aidl 已经确定了接口API&#xff0c;对于native服务端的实现&#xff0c;一般的继承顺序为&#xff1a; 根据继承关系&#xff0c;对于BnXxx/XxxService 对象&…

IO类day02

JAVA IO java io可以让我们用标准的读写操作来完成对不同设备的读写数据工作. java将IO按照方向划分为输入与输出,参照点是我们写的程序. 输入:用来读取数据的,是从外界到程序的方向,用于获取数据. 输出:用来写出数据的,是从程序到外界的方向,用于发送数据. java将IO比喻为…

15个等轴视图设计的电动车汽车无人机等PR剪辑素材视频制作元素

包含15个等轴视图、等距视角电动车、汽车、无人机、沙漏、飞机等PR剪辑素材视频制作元素mogrt动画模板。 特征&#xff1a; 等距设计&#xff1b; 可以更改颜色&#xff1b; 分辨率&#xff1a;全高清&#xff08;19201080&#xff09;&#xff1b; 持续时间&#xff1a;15秒&a…

第四站:C/C++基础-指针

目录 为什么使用指针 函数的值传递&#xff0c;无法通过调用函数&#xff0c;来修改函数的实参 被调用函数需要提供更多的“返回值”给调用函数 减少值传递时带来的额外开销&#xff0c;提高代码执行效率 使用指针前: 使用指针后: 指针的定义: 指针的含义(进阶): 空指针…

【C++进阶04】STL中map、set、multimap、multiset的介绍及使用

一、关联式容器 vector/list/deque… 这些容器统称为序列式容器 因为其底层为线性序列的数据结构 里面存储的是元素本身 map/set… 这些容器统称为关联式容器 关联式容器也是用来存储数据的 与序列式容器不同的是 其里面存储的是<key, value>结构的键值对 在数据检索时…

ARCGIS PRO SDK 设置UI控件状态:启用/禁用

举例&#xff1a; 第一步&#xff1a;添加两个 Button 分别命名为Connect、Disconnect 第二步&#xff1a;nfig.daml添加状态和条件&#xff1a;在 DAML 中定义条件。请记住&#xff0c;条件存在于模块标记<modules>之外&#xff0c;下代码定义&#xff1a;Disconnected_…

【K8S 云原生】Kurbernets集群的调度策略

目录 一、Kubernetes的list-watch机制 1、List-watch 2、创建pod的过程&#xff1a; 二、scheduler调度的过程和策略&#xff1a; 1、简介 2、预算策略&#xff1a;predicate 3、优先策略&#xff1a; 3.1、leastrequestedpriority&#xff1a; 3.2、balanceresourceal…

计操进程同步(信号量pv灵魂三问法狂练版)

文章目录 解题秘诀-灵魂三问法一 同步问题1.1 围棋问题1.2 数据采集问题1.3 三进程文件打印问题1.4 司机售票员问题 二 同步互斥问题2.1 果盘问题 三 同步资源管控问题3.1 兔子问题3.2 数据写入和读取问题3.3 图书馆问题3.4 超市问题3.4.1 解法一3.4.2 解法二 解题秘诀-灵魂三问…

基于ODBC的数据库应用(MFC)

文章目录 1.预备知识1.数据库概述1.数据库和DBMS2.结构化查询语言SQL(Structured Query Language)3.数据库方式种类1.ODBC(Open DataBase Connectivity)开放数据库连接2.DAO(Data Access Objects)数据访问对象3.OLE DB(OLE数据库) 2.MFC ODBC1.CRecordset类构造记录集属性记录集…

c++学习:容器stack栈+queue+map(简易输入法)+deque

目录 stack 模板原型 头文件 模板的成员类型和成员对象和成员函数 栈类模板的容器对象 实例 queue 模板原型 头文件 模板的成员类型和成员对象和成员函数 队列类模板的容器对象 实例 map 模板原型 头文件 模板的成员类型和成员对象和成员函数 关联类模板的容器…

天锐绿盾|绿盾加密软件|电脑文件防泄密|文件加密|图纸加密软件|源代码加密|源代码防泄密系统|公司办公终端核心文件数据\资料防止外泄管理软件系统!

天锐绿盾是一款专业的数据加密和管理软件&#xff0c;旨在保护企业的重要数据不被泄露或损坏。该软件采用了先进的加密技术&#xff0c;确保数据在存储、传输和使用过程中的安全性。同时&#xff0c;天锐绿盾还提供了完善的管理功能&#xff0c;方便企业对加密数据进行统一管理…

分布式系统架构设计之分布式消息队列架构解析

分布式消息队列架构是构建在分布式系统之上的消息队列架构&#xff0c;旨在提高高性能、高可用性和可伸缩性。它包括以下架构相关部分&#xff1a; 1、架构优势 分布式消息队列架构的优势主要体现在以下几个方面&#xff1a; 01 高可用性 在分布式消息队列架构中&#xff0…

Unity组件开发--相机跟随角色和旋转

1.相机跟随组件&#xff0c;节点&#xff1a; 2.相机跟随组件脚本&#xff1a; using System; using System.Collections; using System.Collections.Generic; using Unity.Burst.Intrinsics; using UnityEngine; using UnityEngine.UI;public class CameraFollow : Singleton&…