皮皮网
皮皮网

【源码的要求】【最佳买点源码】【netcat工具源码】引用队列源码_引用队列源码怎么用

来源:jline 源码 发表时间:2024-11-26 15:38:52

1.?引用源码引用源码用??ö???Դ??
2.精读《webreflow》
3.Java中弱引用 丨 12分钟通过案例带你深入源码,分析其原理
4.Vue—关于响应式(二、队列队列异步更新队列原理分析)
5.Redis源码解析:一条Redis命令是引用源码引用源码用如何执行的?

引用队列源码_引用队列源码怎么用

???ö???Դ??

       概述

       在线监控发现OOM涨幅较大,定位修复内存泄漏和大对象占用问题后,队列队列仍未能达到正常标准。引用源码引用源码用在新上报的队列队列源码的要求hprof文件中发现,几乎所有案例中都有名为FinalizerReference的引用源码引用源码用对象,数量庞大,队列队列内存占用高居榜首,引用源码引用源码用判断其为引起OOM上涨的队列队列主因。

       ReferenceQueue

       ReferenceQueue是引用源码引用源码用一个存放Reference对象的队列,当Reference对象所引用的队列队列对象被GC回收时,该Reference对象会被加入到引用队列中。引用源码引用源码用例如,队列队列创建一个bean强引用与一个reference软引用,引用源码引用源码用当bean被回收时,软引用reference对象会被加入queue队列,开发者需自行处理。Leakcanary检测内存泄漏原理基于应用的ReferenceQueue引用队列,例如Activity的引用队列。

       FinalizerReference

       介绍Finalizer对象,指在其Java类中复写了finalize()方法且非空的对象,称作f类。类加载过程中会标记加载的Java类是否为f类。

       FinalizerReference概述

       FinalizerReference是协助FinalizerDaemon线程处理对象finalize()工作的工具。它通过FinalizerReference类创建链表,每个FinalizerReference对象使用ReferenceQueue创建,当对应对象Object referent被回收后,该FinalizerReference会放入ReferenceQueue。

       FinalizerReference.add

       FinalizerReference.add方法由虚拟机调用,创建对象时发现该类为f类,最佳买点源码调用此方法创建FinalizerRefence对象并加入到头链表中。

       FinalizerReference.remove

       当f类对象发生GC时,其对应的FinalizerReference对象会被加入FinalizerReference.queue队列,remove时机与FinalizerDaemon守护线程相关。FinalizerDaemon.runInternal方法通过queue的poll/remove方法获取queue中的Reference引用,执行doFinalize方法调用Finalizer对象的finalize()方法。

       小结

       FinalizerReference主要协助FinalizerDaemon线程执行Finalizer对象的finalize()方法。

       ReferenceQueueDaemon

       FinalizerDaemon守护线程已介绍,这里再看ReferenceQueueDaemon守护线程。创建引用对象时可以关联一个ReferenceQueue队列,被引用对象被GC回收时,该reference对象会被加入其关联队列。加入队列操作由ReferenceQueueDaemon守护线程完成。

       FinalizerWatchdogDaemon

       补充FinalizerWatchdogDaemon守护线程,与FinalizerDaemon和ReferenceQueueDaemon线程一同启动。FinalizerDaemon和ReferenceQueueDaemon线程的runInternal方法中,monitoringNotNeeded方法休眠线程停止timeout计时,此方法唤醒FinalizerWatchdogDaemon守护线程。FinalizerWatchdogDaemon监控两种执行时长:FINALIZER_DAEMON和RQ_DAEMON,执行超时抛出TimeOutException异常,避免在finalize()方法中执行耗时操作。

       OOM排查

       排除大对象和内存泄漏后,在hprof中发现大量X(业务上的某个对象)堆积,X对象对应Java类与Native层有关,重写了finalize()方法,线下无法复现X对象堆积路径。可能的业务场景代码逻辑不当导致X对象疯狂创建,导致FinalizerDaemon线程回收不及时。通过显式调用系统gc和runFinalization方法,发现子线程调用无效,netcat工具源码主线程调用导致ANR。查看ANR堆栈发现问题源于某个finalize()方法调用的Native代码卡死,逻辑问题导致死锁,阻塞FinalizerDaemon线程执行,引起对象堆积。

       总结

       Java中finalizer()实现了类似析构函数的概念,可以在对象被回收前执行回收性操作。f类使用不当可能导致问题,避免重载finalizer()方法,通过逻辑接口释放内存,避免频繁创建或大型对象通过finalizer()释放,以防出现相关问题。

       相关守护线程有四个,可深入查看源码。

       线上监控时,可能还需优化UI渲染、奔溃、卡顿、体积包、网络、存储等,整理成脑图。

       内功修炼需持续不断,性能优化同样需要坚持。

精读《webreflow》

       ç½‘页重排(回流)是阻碍流畅性的重要原因之一,结合Whatforceslayout/reflow这篇文章与引用,整理一下回流的起因与优化思考。

       å€Ÿç”¨è¿™å¼ ç»å…¸å›¾ï¼š

       ç½‘页渲染会经历DOM->CSSOM->Layout(重排orreflow)->Paint(重绘)->Composite(合成),其中Composite在精读《深入了解现代浏览器四》详细介绍过,是在GPU进行光栅化。

       é‚£ä¹ˆæŽ’除JS、DOM、CSSOM、Composite可能导致的性能问题外,剩下的就是我们这次关注的重点,reflow了。从顺序上可以看出来,重排后一定重绘,而重绘不一定触发重排。

概述

       ä»€ä¹ˆæ—¶å€™ä¼šè§¦å‘Layout(reflow)呢?一般来说,当元素位置发生变化时就会。但也不尽然,因为浏览器会自动合并更改,在达到某个数量或时间后,会合并为一次reflow,而reflow是渲染页面的重要一步,打开浏览器就一定会至少reflow一次,所以我们不可能避免reflow。

       é‚£ä¸ºä»€ä¹ˆè¦æ³¨æ„reflow导致的性能问题呢?这是因为某些代码可能导致浏览器优化失效,即明明能合并reflow时没有合并,这一般出现在我们用jsAPI访问某个元素尺寸时,为了保证拿到的是精确值,不得不提前触发一次reflow,即便写在for循环里。

       å½“然也不是每次访问元素位置都会触发reflow,在浏览器触发reflow后,所有已有元素位置都会记录快照,只要不再触发位置等变化,第二次开始访问位置就不会触发reflow,关于这一点会在后面详细展开。现在要解释的是,这个”触发位置等变化“,到底有哪些?

       æ ¹æ®Whatforceslayout/reflow文档的总结,一共有这么几类:

获得盒子模型信息

       elem.offsetLeft,elem.offsetTop,elem.offsetWidth,elem.offsetHeight,elem.offsetParent

       elem.clientLeft,elem.clientTop,elem.clientWidth,elem.clientHeight

       elem.getClientRects(),elem.getBoundingClientRect()

       èŽ·å–元素位置、宽高的一些手段都会导致reflow,不存在绕过一说,因为只要获取这些信息,都必须reflow才能给出准确的值。

滚动

       elem.scrollBy(),elem.scrollTo()

       elem.scrollIntoView(),elem.scrollIntoViewIfNeeded()

       elem.scrollWidth,elem.scrollHeight

       elem.scrollLeft,elem.scrollTop访问及赋值

       å¯¹scrollLeft赋值等价于触发scrollTo,所有导致滚动产生的行为都会触发reflow,笔者查了一些资料,目前主要推测是滚动条出现会导致可视区域变窄,所以需要reflow。

focus()

       elem.focus()(源码)

       å¯ä»¥æ ¹æ®æºç çœ‹ä¸€ä¸‹æ³¨é‡Šï¼Œä¸»è¦æ˜¯è¿™ä¸€æ®µï¼š

//Ensurewehavecleanstyle(includingforceddisplaylocks).GetDocument().UpdateStyleAndLayoutTreeForNode(this)

       å³åœ¨èšç„¦å…ƒç´ æ—¶ï¼Œè™½ç„¶æ²¡æœ‰æ‹¿å…ƒç´ ä½ç½®ä¿¡æ¯çš„诉求,但指不定要被聚焦的元素被隐藏或者移除了,此时必须调用UpdateStyleAndLayoutTreeForNode重排重绘函数,确保元素状态更新后才能继续操作。

       è¿˜æœ‰ä¸€äº›å…¶ä»–elementAPI:

       elem.computedRole,elem.computedName

       elem.innerText(源码)

       innerText也需要重排后才能拿到正确内容。

获取window信息

       window.scrollX,window.scrollY

       window.innerHeight,window.innerWidth

       window.visualViewport.height/width/offsetTop/offsetLeft(源码)

       å’Œå…ƒç´ çº§åˆ«ä¸€æ ·ï¼Œä¸ºäº†æ‹¿åˆ°æ­£ç¡®å®½é«˜å’Œä½ç½®ä¿¡æ¯ï¼Œå¿…须重排。

document相关

       document.scrollingElement仅重绘

       document.elementFromPoint

       elementFromPoint因为要拿到精确位置的元素,必须重排。

Form相关

       inputElem.focus()

       inputElem.select(),textareaElem.select()

       focus、select触发重排的原因和elem.focus类似。

鼠标事件相关

       mouseEvt.layerX,mouseEvt.layerY,mouseEvt.offsetX,mouseEvt.offsetY(源码)

       é¼ æ ‡ç›¸å…³ä½ç½®è®¡ç®—,必须依赖一个正确的排布,所以必须触发reflow。

getComputedStyle

       getComputedStyle通常会导致重排和重绘,是否触发重排取决于是否访问了位置相关的key等因素。

Range相关

       range.getClientRects(),range.getBoundingClientRect()

       èŽ·å–选中区域的大小,必须reflow才能保障精确性。

SVG

       å¤§é‡SVG方法会引发重排,就不一一枚举了,总之使用SVG操作时也要像操作dom一样谨慎。

contenteditable

       è¢«è®¾ç½®ä¸ºcontenteditable的元素内,包括将图像复制到剪贴板在内,大量操作都会导致重排。(源码)

精读

       Whatforceslayout/reflow下面引用了几篇关于reflow的相关文章,笔者挑几个重要的总结一下。

repaint-reflow-restyle

       repaint-reflow-restyle提到现代浏览器会将多次dom操作合并,但像IE等其他内核浏览器就不保证有这样的实现了,因此给出了一个安全写法:

//badvarleft=,top=;el.style.left=left+"px";el.style.top=top+"px";//betterel.className+="theclassname";//orwhentopandleftarecalculateddynamically...//betterel.style.cssText+=";left:"+left+"px;top:"+top+"px;";

       æ¯”如用一次className的修改,或一次cssText的修改保证浏览器一定触发一次重排。但这样可维护性会降低很多,不太推荐。

avoidlargecomplexlayouts

       avoidlargecomplexlayouts重点强调了读写分离,首先看下面的badcase:

functionresizeAllParagraphsToMatchBlockWidth(){ //Putsthebrowserintoaread-write-read-writecycle.for(vari=0;i<paragraphs.length;i++){ paragraphs[i].style.width=box.offsetWidth+'px';}}

       åœ¨for循环中不断访问元素宽度,并修改其宽度,会导致浏览器执行N次reflow。

       è™½ç„¶å½“JavaScript运行时,前一帧中的所有旧布局值都是已知的,但当你对布局做了修改后,前一帧所有布局值缓存都会作废,因此当下次获取值时,不得不重新触发一次reflow。

       è€Œè¯»å†™åˆ†ç¦»çš„话,就代表了集中读,虽然读的次数还是那么多,但从第二次开始就可以从布局缓存中拿数据,不用触发reflow了。

       å¦å¤–还提到flex布局比传统float重排速度快很多(3msvsms),所以能用flex做的布局就尽量不要用float做。

reallyfixinglayoutthrashing

       reallyfixinglayoutthrashing提到了用fastdom实践读写分离:

ids.forEach(id=>{ fastdom.measure(()=>{ consttop=elements[id].offsetTopfastdom.mutate(()=>{ elements[id].setLeft(top)})})})

       fastdom是一个可以在不分离代码的情况下,分离读写执行的库,尤其适合用在reflow性能优化场景。每一个measure、mutate都会推入执行队列,并在window.requestAnimationFrame时机执行。

总结

       å›žæµæ— æ³•é¿å…ï¼Œä½†éœ€è¦æŽ§åˆ¶åœ¨æ­£å¸¸é¢‘率范围内。

       æˆ‘们需要学习访问哪些属性或方法会导致回流,能不使用就不要用,尽量做到读写分离。在定义要频繁触发回流的元素时,尽量使其脱离文档流,减少回流产生的影响。

       è®¨è®ºåœ°å€æ˜¯ï¼šç²¾è¯»ã€Šwebreflow》·Issue#·dt-fe/weekly

       å¦‚果你想参与讨论,请点击这里,每周都有新的主题,周末或周一发布。前端精读-帮你筛选靠谱的内容。

       ç‰ˆæƒå£°æ˜Žï¼šè‡ªç”±è½¬è½½-非商用-非衍生-保持署名(创意共享3.0许可证)

原文:/post/

Java中弱引用 丨 分钟通过案例带你深入源码,分析其原理

       深入理解Java中的弱引用:分钟带你探索原理与应用

       弱引用在Java中扮演着微妙的角色,它并非阻止垃圾回收,而是提供了一种特殊关联方式。JDK官方解释,弱引用主要用于实现那些不需要阻止其键或值被回收的买去网 源码映射。弱引用的出现,是为了在不再使用对象时,让垃圾回收器在合适的时候自动回收,从而避免内存溢出问题。

       让我们通过实例来了解。想象一个场景,当我们维护一个map,存储了大量生命周期短暂的对象,如果key和value都由强引用指向,即使我们设置为null,对象仍不会被回收,因为map作为静态变量,其生命周期长。这时,弱引用的介入就显得尤为重要。通过将key变为弱引用,即使对象不再被方法引用,也能在垃圾回收时被释放,避免内存耗尽。

       弱引用的使用并不复杂,只需将HashMap替换为WeakHashMap,将key变为WeakReference。当我们不再需要这些对象时,它们会被自动回收,如在上述例子中,输出的size为0,就证明了这一点。然而,这并不意味着value和entry会自动回收,这时WeakHashMap的嵌套网页源码expungeStaleEntries方法就发挥作用,它会清理不再引用的对象。

       引用队列在此过程中扮演了关键角色,它帮助我们在弱引用被回收时高效地找到并处理相关对象,避免了遍历整个数据结构的性能消耗。在使用弱引用时,需要注意检查对象是否已被回收,以防空指针异常。

       通过这些深入解析,我们对弱引用有了全面的认识,它在内存管理中的巧妙应用,为我们提供了一种解决内存溢出的有效手段。

Vue—关于响应式(二、异步更新队列原理分析)

       本节学习要点:Event Loop、Promise

       关于Event Loop的介绍,可以参考阮一峰老师的文章。

       关于Promise,请访问:developer.mozilla.org/z...

       上一节介绍了Vue通过Object.defineProperty拦截数据变化的响应式原理,数据变化后会触发notify方法来通知变更。这一节将继续分析,收到通知后Vue会开启一个异步更新队列。

       以下是两个问题:

       一、异步更新队列

       首先看一段代码演示。

       将上一节的代码拿过来,假设我们现在不仅依赖x,还有y、z,分别将x、y、z输出到页面上。我们现在依赖了x、y、z三个变量,那么我们应该把onXChange函数名改为watch,表示它可以监听变化,而不仅仅是监听一个x的变化。

       可以看到这三个值都被打印在页面上。

       现在我们对x、y、z的value进行修改。

       查看页面,结果没有问题,每个数据的变化都被监听到并且进行了响应。

       既然结果是对的,那我们的问题是什么?

       这个问题是:每次数据变化都进行了响应,每次都渲染了模板,如果数据变化了一百次、一千次呢?难道要重复渲染一百遍、一千遍吗?

       我们都知道频繁操作DOM会影响网页性能,涉及重排和重绘的知识感兴趣请阅读阮一峰老师的文章:ruanyifeng.com/blog/...

       因此,既要保证所有的依赖都准确更新,又要保证不能频繁渲染成为了首要问题。现在我们修改x.value、y.value、z.value都是同步通知依赖进行更新的,有没有一种机制可以等到我修改这些值之后再执行更新任务呢?

       这个答案是——异步。

       异步任务会等到同步任务清空后执行,借助这个特点和我们前面的分析,我们需要:

       按照步骤,我们创建如下代码:

       接着我们需要修改一下notify的代码,监听到数据变化后不立即调用依赖进行更新,而是将依赖添加到队列中。

       回到页面,我们发现页面上还是重复渲染了三次模板。

       那么我们写的这段代码有什么用呢?异步又体现在哪里呢?接着往下看。

       二、nextTick原理分析

       上面的代码中,虽然我们开启了一个队列,并且成功将任务推入队列中进行执行,但本质上还是同步推入和执行的。我们要让它变成异步队列。

       于是到了Promise发挥作用的时候了。关于宏任务和微任务的介绍请参考:zhuanlan.zhihu.com/p/...

       我们创建nextTick函数,nextTick接收一个回调函数,返回一个状态为fulfilled的Promise,并将回调函数传给then方法。

       然后只需要在添加任务时调用nextTick,将执行任务的flushJobs函数传给nextTick即可。

       回到页面。

       虽然修改了x、y、z三个变量的value,最后页面上只渲染了一次。

       再来总结一下这段代码的执行过程:

       这也正是Vue采用的解决方案——异步更新队列,官方文档描述得很清楚。

       文档地址:cn.vuejs.org/v2/guide/r...

       三、结合Vue源码来看nextTick

       在Vue中,我们可以通过两种方式来调用nextTick:

       (至于什么时候使用nextTick,如果你不偷懒看了官方文档的话,都能找到答案哈哈)

       以下源码节选自vue2.6.版本,这两个API分别在initGlobalAPI函数和renderMixin函数中挂载,它们都引用了nextTick函数。

       nextTick源码如下:

       在内部,它访问了外部的callbacks,这个callbacks就是前面提到的队列,nextTick一调用就给队列push一个回调函数,然后判断pending(pending的作用就是控制同一时间内只执行一次timerFunc),调用timerFunc(),最后返回了一个Promise(使用过nextTick的应该都知道吧)。

       我们来看一下callbacks、pending、timerFunc是如何定义的。

       可以看到timerFunc函数只是调用了p.then方法并将flushCallbacks函数推入了微任务队列,而p是一个fulfilled状态的Promise,与我们自己的nextTick功能一致。

       这个flushCallbacks函数又干了什么呢?

       flushCallbacks中重新将pending置为初始值,复制callbacks队列中的任务后将队列清空,然后依次执行复制的任务,与我们自己的flushJobs函数功能一致。

       看完上面的源码,可以总结出Vue是这么做的,又到了小学语文之——提炼中心思想的时候了。

       对比一下我们自己写的代码,你学会了吗?

       以上演示代码已上传github:github.com/Mr-Jemp/VueS...

       后面要学习的内容在这里:

       Vue—关于响应式(三、Diff Patch原理分析)

       Vue—关于响应式(四、深入学习Vue响应式源码)

       本文由博客一文多发平台OpenWrite发布!

Redis源码解析:一条Redis命令是如何执行的?

       作者:robinhzhang

       Redis,一个开源内存数据库,凭借其高效能和广泛应用,如缓存、消息队列和会话存储,本文将带你探索其命令执行的底层流程。本文将以源码解析的形式,逐层深入Redis的核心结构和命令执行过程,旨在帮助开发者理解实现细节,提升编程技术和设计意识。

       源码结构概览

       在学习Redis源代码之前,首先要了解其主要的组成部分:redisServer、redisClient、redisDb、redisObject以及aeEventLoop。这些结构体和事件模型构成了Redis的核心架构。

       redisServer:服务端运行的核心结构,包括监听socket、数据存储的redisDb列表和客户端连接信息。

       redisClient:客户端连接状态的存储,包括命令处理缓冲区、回复数据列表和数据库句柄。

       redisDb:键值对的数据存储,采用两个哈希表实现渐进式rehash。

       redisObject:存储对象的通用表示,包含引用计数和LRU时间,用于内存管理。

       aeEventLoop:事件循环,管理文件和时间事件的处理。

       核心流程详解

       Redis的执行流程从main函数开始,首先初始化配置和服务器组件,进入主循环处理事件。命令执行流程涉及redis启动、客户端连接、接收命令和返回结果四个步骤:

       启动阶段:创建socket服务器,注册可读事件,进入主循环。

       连接阶段:客户端连接后,接收并处理命令,创建客户端实例。

       命令阶段:客户端发送命令,服务端解析并调用对应的命令处理函数。

       结果阶段:处理命令后,根据协议格式构建回复并写回客户端。

       渐进式rehash与内存管理

       Redis的内存管理采用引用计数法,通过对象的refcount字段控制内存分配和释放。rehash操作在Redis 2.x版本引入,通过逐步迁移键值对,降低对单线程性能的影响。当负载达到阈值,会进行扩容,这涉及新表的创建和键值对的迁移。

       总结

       本文通过Redis源码分析,揭示了其命令执行的细节,包括启动流程、客户端连接、命令处理和结果返回,以及内存管理策略。这将有助于开发者深入理解Redis的工作原理,提升编程效率和设计决策能力。

相关栏目:焦点