深入理解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 {
    @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);
    }
}

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

真的干不过,00后整顿职场已经给我卷麻了,想离职了...

在程序员职场上&#xff0c;什么样的人最让人反感呢? 是技术不好的人吗?并不是。技术不好的同事&#xff0c;我们可以帮他。 是技术太强的人吗?也不是。技术很强的同事&#xff0c;可遇不可求&#xff0c;向他学习还来不及呢。 真正让人反感的&#xff0c;是技术平平&#x…

J2EE 实验一

实验一 基于Struts2的登录系统实现 一、目的与任务 目的&#xff1a;熟悉在集成开发平台上配置Struts2框架&#xff0c;学习Struts2编程 任务&#xff1a;在集成开发平台上创建Struts2项目&#xff0c;实现登录系统 二、内容、要求与安排方式 1、实验内容与要求&#xff1…

C盘突然满了,怎么清理

方法一 winr 输入%tem%按回车键&#xff0c;出现的这些都是缓存文件可以按删除键删掉 方法二 winr 输入cleanmgr按回车键&#xff0c;选择清理的盘符&#xff0c;这里选择C盘&#xff0c;点击确定删除 方法三 在系统设置里手动删除 找到电脑里面的设置选项&#xff0c;找…

第九节HarmonyOS 常用基础组件12-TextTimer

1、描述 通过文本显示计时信息并控制其计时器状态的组件。 2、接口 TextTimer(options?: {isCountDown?: boolean, count?: number, controller?: TextTimerController}) 3、参数 参数名称 参数类型 必填 描述 isCountDown boolean 否 是否倒计时。默认值&#…

LeetCode刷题--- 珠宝的最高价值

个人主页&#xff1a;元清加油_【C】,【C语言】,【数据结构与算法】-CSDN博客 个人专栏 力扣递归算法题 http://t.csdnimg.cn/yUl2I 【C】 ​​​​​​http://t.csdnimg.cn/6AbpV 数据结构与算法 ​​​http://t.csdnimg.cn/hKh2l 前言&#xff1a;这个专栏主要讲述动…

如何启用Windows电脑的内置Administrator账户

前言 不知道从什么时候开始&#xff0c;新电脑或者新系统开机之后都会出现一个界面让你创建一个账户&#xff0c;但这个账户有可能是本地账户&#xff08;Windows10&#xff09;还有强制你登录微软账户的&#xff08;Windows11&#xff09;。 好像曾经熟悉的电脑Administrator…

750ml离心瓶进口国产离心机通用750ml离心杯高低速离心机瓶

750ml低速离心瓶系列&#xff1a; 产品货号&#xff1a;ZY1136222 材质&#xff1a;PPCO 容量&#xff08;ml&#xff09;&#xff1a;750 尺寸&#xff08;O.DxH,mm&#xff09;&#xff1a;98.5x147 最大离心力&#xff08;xg&#xff09;&#xff1a;6000 产品货号&…

Vue2:通过props给组件传数据

一、业务场景 我们在使用Vue组件时&#xff0c;常常会复用Vue组件&#xff0c;那么&#xff0c;问题来了&#xff0c;复用的时候&#xff0c;业务数据不相同&#xff0c;怎么办了&#xff1f; 这里我们就需要学习新的属性&#xff1a;props来实现这个功能。 这样&#xff0c;组…

代码随想录day23 二叉岁终章

669. 修剪二叉搜索树 题目 给定一个二叉搜索树&#xff0c;同时给定最小边界L 和最大边界 R。通过修剪二叉搜索树&#xff0c;使得所有节点的值在[L, R]中 (R>L) 。你可能需要改变树的根节点&#xff0c;所以结果应当返回修剪好的二叉搜索树的新的根节点。 思考 这题有个…

ComfyUI报错AttributeError: module ‘cv2.gapi.wip.draw‘ has no attribute ‘Text‘

ComfyUI在安装comfyui-reactor-node插件,然后启动之后突然报错: AttributeError: module cv2.gapi.wip.draw has no attribute Text 这是怎么回事呢? 于是四处搜寻答案。 总之就是opencv-python版本的问题导致的。 我将有可能解决办法的方法进行了总结。 下面列出所有解…

数字人源码部署新机遇,预测2024年AI应用将出现爆发式增长

近日&#xff0c;钉钉联合IDC发布《2024 AIGC应用层十大趋势白皮书》&#xff0c;其中&#xff0c;IDC预测到&#xff0c;2024年AI应用将出现爆发式增长&#xff0c;到2024年全球将涌现出超过5亿个新应用&#xff0c;到2026年&#xff0c;2/3的云应用将使用AI。 看来&#xff…

selenium如何使用隧道代理请求目标地址?

使用Selenium结合隧道代理IP可以通过以下步骤实现&#xff1a; 获取代理IP&#xff1a; 首先&#xff0c;你需要获得一个可用的隧道代理IP。你可以使用代理服务提供商&#xff08;巨量IP平台提供免费隧道代理测试&#xff09; 安装Selenium&#xff1a; 如果你还没有安装Selen…

基于CNC车间的复合机器人柔性上下料系统改造方案

在制造业中&#xff0c;CNC车间一直面临着提高生产效率、降低人工成本和提升柔性生产能力的挑战。针对这些行业痛点&#xff0c;富唯智能为您提供一种创新的解决方案&#xff1a;复合机器人柔性上下料系统。本方案结合了先进的机器人技术和自动化系统&#xff0c;旨在提高生产效…

Qt 6之五:创建菜单

Qt 6之五&#xff1a;创建菜单 Qt是一种跨平台的C应用程序开发框架&#xff0c;它提供了一套丰富的工具和库&#xff0c;可以帮助开发者快速构建跨平台的应用程序&#xff0c;用于开发图形用户界面&#xff08;GUI&#xff09;和非GUI应用程序。 Qt 6之一&#xff1a;简介、安…

LabVIEW开发分布式光纤油气管道泄漏检测及预警系统

LabVIEW开发分布式光纤油气管道泄漏检测及预警系统 随着油气工业的发展&#xff0c;管道泄漏成为一个严峻的安全问题。本文介绍了一种基于LabVIEW的分布式光纤油气管道泄漏检测及预警系统的设计思路和组成结构。系统包括硬件和软件两部分&#xff0c;其中硬件部分详细阐述了分…

【python】爬取知乎热榜Top50保存到Excel文件中【附源码】

欢迎来到英杰社区https://bbs.csdn.net/topics/617804998 一、导入必要的模块&#xff1a; 这篇博客将介绍如何使用Python编写一个爬虫程序&#xff0c;从斗鱼直播网站上获取图片信息并保存到本地。我们将使用requests模块发送HTTP请求和接收响应&#xff0c;以及os模块处理文件…

一口气搞懂【Linux内存管理】,就靠这60张图、59个问题了

按&#xff1a;基于x86处理器上&#xff0c;以系统启动过程中内存管理的逐步构建为主轴&#xff0c;分析内存的管理方式与其相关的安全防护功能。 1、如何知道计算机内存布局&#xff1f;内存空间有多少&#xff1f; 春江水暖鸭先知&#xff0c;计算机上电启动的时候&#xf…

微信预约小程序制作指南:从小白到专家

在当今的数字时代&#xff0c;微信小程序已经成为了一种非常流行的应用方式。预约功能更是成为了许多小程序的核心功能之一。如果你也想为你的小程序添加预约功能&#xff0c;以下步骤将会对你有所帮助。 一、进入乔拓云网后台 乔拓云网是一个在线小程序开发平台&#xff0c;你…

.pings勒索病毒解密方法|勒索病毒解决|勒索病毒恢复|数据库修复

导言&#xff1a; 随着科技的发展&#xff0c;网络空间中的威胁也日益猖獗&#xff0c;其中之一就是勒索病毒&#xff0c;而.pings 勒索病毒则是其中的一种。本文将深入介绍.pings 勒索病毒的特征、恢复被其加密的数据文件的方法&#xff0c;并提供预防措施&#xff0c;以保障…

【redis】Redis中的字典类型:数据结构与使用方法

文章目录 Redis中的字典类型&#xff1a;数据结构与使用方法简介如何提高哈希表性能如何使用 Redis中的字典类型&#xff1a;数据结构与使用方法 简介 Redis中的字典类型的底层实现是哈希表&#xff08;Hash Table&#xff09;。 Redis的字典使用哈希表作为底层实现&#xf…