皮皮网
皮皮网

【供求网 源码】【源码求反码补码】【ssr节点网站源码】引用队列源码_引用队列源码怎么用

来源:幻境网盾源码 发表时间:2024-12-22 13:37:30

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方法,发现子线程调用无效,ssr节点网站源码主线程调用导致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的github系统的源码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的工作原理,提升编程效率和设计决策能力。

相关栏目:焦点