一文搞懂JavaScript中的内存泄露

目录
  • 什么是内存泄漏
  • 怎么检测内存泄漏
    • Performance
    • Memory
  • 内存泄漏的场景
  • 垃圾回收算法
    • 引用计数
    • 循环引用
    • 标记清除
  • 闭包是内存泄漏吗
  • 总结

以前我们说的内存泄漏,通常发生在后端,但是不代表前端就不会有内存泄漏。特别是当前端项目变得越来越复杂后,前端也逐渐称为内存泄漏的高发区。本文就带你认识一下Javascript的内存泄漏。

什么是内存泄漏

什么是内存?内存其实就是程序在运行时,系统为其分配的一块存储空间。每一块内存都有对应的生命周期:

  • 内存分配:在声明变量、函数时,系统分配的内存空间
  • 内存使用:对分配到的内存进行读/写操作,即访问并使用变量、函数等
  • 释放内存:内存使用完毕后,释放掉不再被使用的内存

不像C语言等底层语言需要程序员在开发的时候自己通过mallocfree来申请或者释放内存,JavaScript同大多数现代编程语言一样,都实现了给变量自动分配内存,并且在不使用变量的时候“自动”释放内存,这个释放内存的过程就被称为垃圾回收。

每一个程序的运行都需要一块内存空间,如果某一块内存空间在使用后未被释放,并且持续累积,导致未释放的内存空间越积越多,直至用尽全部的内存空间。程序将无法正常运行,直观体现就是程序卡死,系统崩溃,这一现象就被称为内存泄漏。

我们来举几个例子说明一下:

内存分配

const obj = {
    name: '张三'
} // 给{name: '张三'}分配内存
function foo() {
    console.log('hello world')
} // 给函数分配内存
let date = new Date(); // 根据函数返回的结果创建变量,会分配一个Date对象

可能发生内存泄漏

function foo() {
    const obj = {name: '张三'}
    window.obj = obj;
    console.log(obj)
}
foo(); // foo()执行完毕,{name: '张三'}对应的内存空间本应该被释放,但是由于又被全局变量所引用,因此其对应的内存空间不会被垃圾回收

闭包的内存占用

function bar() {
    const data = {}
    return {
        get(key) {
            return data[key]
        },
        set(key, value) {
            data[key] = value
        }
    }; // 闭包对象
}
const {get, set} = bar; // 结构
set('name', '张三')
get('name'); // 张三
// 函数执行完毕,data对象并不会被垃圾回收,这是闭包的机制

内存泄漏听起来可能会有点抽象,怎么能比较直观的看到内存泄漏的过程呢?

怎么检测内存泄漏

内存泄漏主要是指的是内存持续升高,但是如果是正常的内存增长的话,不应该被当作内存泄漏来排查。排查内存泄漏,我们可以借助Chrome DevTools的Performance和Memory选项。举个栗子:

我们新建一个memory.html的文件

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
  <style>
    body {
      text-align: center;
    }
  </style>
</head>
<body>
  <p>检测内存变化</p>
  <button id="btn">开始</button>
  <script>
    const arr = [];
    // 数组中添加100万个数据
    for (let i = 0; i < 100 * 10000; i++) {
      arr.push(i)
    }
    function bind() {
      const obj = {
        str: JSON.stringify(arr) // 浅拷贝的方式创建一个比较大的字符串
      }
      // 每次调用bind函数,都在全局绑定一个onclick监听事件,不一定非要执行
      // 使用绑定事件,主要是为了保持obj被全局标记
      window.addEventListener('click', () => {
        // 引用对象obj
        console.log(obj);
      })
    }
    let n = 0;
    function start() {
      setTimeout(() => {
        bind(); // 调用bind函数
        n++; // 循环次数增加
        if (n < 50) {
          start(); // 循环执行50次,注意这里并没有使用setInterval定时器
        } else {
          alert('done');
        }
      }, 200);
    }
    document.getElementById('btn').addEventListener('click', () => {
      start();
    })
  </script>
</body>
</html>

在无法确定是否发生内存泄漏时,我们可以先使用Performance来录制一段页面加载的性能变化,先判断是否有内存泄漏发生。

Performance

本次案例仅以Chrome浏览器展开描述,其他浏览器可能会有些许差异。首先我们鼠标右键选择检查或者直接F12进入DevTools页面,面板上选择Performance,选择后应该是如下页面:

在开始之前,我们先点击一下Collect garbageclear来保证内存干净,没有其他遗留内存的干扰。然后我们点击Record来开始录制,并且同时我们也要点击页面上的开始按钮,让我们的代码跑起来。等到代码结束后,我们再点击Record按钮以停止录制,录制的时间跟代码执行的时间相比会有出入,只要保证代码是完全执行完毕的即可。停止录制后,我们会得到如下的结果:

Performance的内容很多,我们只需要关注内存的变化,由此图可见,内存这块区域的曲线是在一直升高的并且到达顶点后并没有回落,这就有可能发生了内存泄漏。因为正常的内存变化曲线应该是类似于“锯齿”,也就是有上有下,正常增长后会有一定的回落,但不一定回落到和初始值一样。而且我们还可以隐约看到程序运行结束后,内存从初始的6.2MB增加到了差不多351MB,这个数量级的增加还是挺明显的。我们只是执行了50次循环,如果执行的次数更多,将会耗尽浏览器的内存空间,导致页面卡死。

虽然是有内存泄漏,但是如果我们想进一步看内存泄漏发生的地方,那么Performance就不够用了,这个时候我们就需要使用Memory面板。

Memory

DevTools的Memory选项主要是用来录制堆内存的快照,为的是进一步分析内存泄漏的详细信息。有人可能会说,为啥不一开始就直接使用Memory呢,反而是先使用Performance。因为我们刚开始就说了,内存增长不表示就一定出现了内存泄漏,有可能是正常的增长,直接使用Memory来分析可能得不到正确的结果。

我们先来看一下怎么使用Memory

首先选择Memory选项,然后清除缓存,在配置选项中选择堆内存快照。内存快照每次点击录制按钮都会记录当前的内存使用情况,我们可以在程序开始前点击一下记录初始的内存使用,代码结束后再点一下记录最终的内存使用,中间可以点击也可以不点击。最后在快照列表中至少可以得到两个内存记录:

初始内存我们暂时不深究,我们选择列表的最后一条记录,然后在筛选下拉框选择最后一个,即第一个快照和第二个快照的差异。

这里我们重点说一下Shallow SizeRetained Size的区别:

  • Shallow Size:对象自身占用的内存大小,一般来说字符串、数组的Shallow Size都会比较大
  • Retained Size:这个是对象自身占用的内存加上无法被GC释放的内存的大小,如果Retained Size和Shallow Size相差不大,基本上可以判定没有发生内存泄漏,但是如果相差很大,例如上图的Object,这就表明发生了内存泄漏。

我们再来细看一下Object,任意展开一个对象,可以在树结构中发现每一个对象都有一个全局事件绑定,并且占用了较大的内存空间。解决本案例涉及的内存泄漏也比较简单,就是及时释放绑定的全局事件。

关于PerformanceMemory的详细使用可以参考:手把手教你排查Javascript内存泄漏

内存泄漏的场景

大多数情况下,垃圾回收器会帮我们及时释放内存,一般不会发生内存泄漏。但是有些场景是内存泄漏的高发区,我们在使用的时候一定要注意:

我们在开发的时候经常会使用console在控制台打印信息,但这也会带来一个问题:被console使用的对象是不能被垃圾回收的,这就可能会导致内存泄漏。因此在生产环境中不建议使用console.log()的理由就又可以加上一条了

被全局变量、全局函数引用的对象,在Vue组件销毁时未清除,可能会导致内存泄漏

// Vue3
<script setup>
import {onMounted, onBeforeUnmount, reactive} from 'vue'
const arr = reactive([1,2,3]);
onMounted(() => {
    window.arr = arr; // 被全局变量引用
    window.arrFunc = () => {
        console.log(arr); // 被全局函数引用
    }
})
// 正确的方式
onBeforeUnmount(() => {
    window.arr = null;
    window.arrFunc = null;
})
</script>

定时器未及时在Vue组件销毁时清除,可能会导致内存泄漏

// Vue3
<script setup>
import {onMounted, onBeforeUnmount, reactive} from 'vue'
const arr = reactive([1,2,3]);
const timer = reactive(null);
onMounted(() => {
    setInterval(() => {
        console.log(arr); // arr被定时器占用,无法被垃圾回收
    }, 200);
    // 正确的方式
    timer = setInterval(() => {
        console.log(arr);
    }, 200);
})
// 正确的方式
onBeforeUnmount(() => {
    if (timer) {
        clearInterval(timer);
        timer = null;
    }
})
</script>

setTimeoutsetInterval两个定时器在使用时都应该注意是否需要清理定时器,特别是setInterval,一定要注意清除。

绑定的事件未及时在Vue组件销毁时清除,可能会导致内存泄漏

绑定事件在实际开发中经常遇到,我们一般使用addEventListener来创建。

// Vue3
<script setup>
import {onMounted, onBeforeUnmount, reactive} from 'vue'
const arr = reactive([1,2,3]);
const printArr = () => {
    console.log(arr)
}
onMounted(() => {
    // 监听事件绑定的函数为匿名函数,将无法被清除
    window.addEventListener('click', () => {
        console.log(arr); // 全局绑定的click事件,arr被引用,将无法被垃圾回收
    })
    // 正确的方式
    window.addEventListener('click', printArr);
})
// 正确的方式
onBeforeUnmount(() => {
    // 注意清除绑定事件需要前后是同一个函数,如果函数不同将不会清除
    window.removeEventListener('click', printArr);
})
</script>

被自定义事件引用,在Vue组件销毁时未清除,可能会导致内存泄漏

自定义事件通过emit/on来发起和监听,清除自定义事件和绑定事件差不多,不同的是需要调用off方法

// Vue3
<script setup>
import {onMounted, onBeforeUnmount, reactive} from 'vue'
import event from './event.js'; // 自定义事件
const arr = reactive([1,2,3]);
const printArr = () => {
    console.log(arr)
}
onMounted(() => {
    // 使用匿名函数,会导致自定义事件无法被清除
    event.on('printArr', () => {
        console.log(arr)
    })
    // 正确的方式
    event.on('printArr', printArr)
})
// 正确的方式
onBeforeUnmount(() => {
    // 注意清除自定义事件需要前后是同一个函数,如果函数不同将不会清除
    event.off('printArr', printArr)
})
</script>

除了及时清除监听器、事件等,对于全局变量的引用,我们可以选择WeakMapWeakSet等弱引用数据类型。这样的话,即使我们引用的对象数据要被垃圾回收,弱引用的全局变量并不会阻止GC。

垃圾回收算法

我们知道了内存泄漏的含义,也知道了怎么来检测内存泄漏,甚至可以一定程度上规避内存泄漏了。除了那些容易产生内存泄漏的场景,js是使用什么样的机制来保证垃圾会被尽可能的回收呢?垃圾回收算法早期使用的是引用计数,现在主流都采用标记清除的方式了。

引用计数

我们创建一个对象,js会在堆内存中分配一块区域用于存储对象信息,并且在栈中存在对象数据的引用地址。举个栗子:

let obj = {name: '张三'};
let obj2 = obj;
// 对象数据{name: '张三'}被obj和obj2引用,引用计数为2,此时{name: '张三'}不能被垃圾回收
obj = 0; // obj虽然不引用{name: '张三'},但是obj2还在引用,此时{name: '张三'}也不能被垃圾回收
obj2 = 0; // 此时的{name: '张三'}已经是零引用了,可以被垃圾回收

下图为创建变量时的内存管理:

对于函数来说,正常情况下函数执行完毕,其占用的内存就会被垃圾回收:

function foo() {
    const obj = {name: '张三'}
    console.log(obj)
}
foo(); // 函数执行完毕,obj作为局部变量,会随着函数的结束而结束,{name: '张三'}由于零引用,其占用的内存空间会被释放

但是如果函数中存在全局引用,那么函数结束后,全局引用占用的内存将无法被释放

function foo() {
    const obj = {name: '张三'}
    window.obj = obj;
    console.log(obj)
}
foo(); // 函数执行完毕,{name: '张三'}被全局引用

引用计数有一个缺陷,那就是无法处理循环引用。

循环引用

循环引用指的是两个或者多个对象之间,存在相互引用,并形成了一个循环。如果是在函数中,函数运行结束后应该释放掉所有局部变量引用的对象,但是按照引用计数算法,循环引用间并不是零引用,因此它们就不会被释放。举个例子:

function foo() {
    const obj = {name: '张三'};
    const obj2 = {age: 0};
    obj.a = obj2; // obj对象引用了obj2
    obj2.a = obj; // obj2引用了obj
}
foo(); // 由于存在循环引用,{name: '张三'}和{age: 0}所占用的堆内存都不会被释放

上面的例子可能比较抽象,我们再来举一个实际的例子。在IE6、7版本中(IE已于2022年6月15日正式退出了历史舞台),在对DOM对象进行垃圾回收时,就有可能因为循环引用导致内存泄漏。

let div = document.getElementById('div1');
div.a = div; // 循环引用自己
div.someBigData = new Array(10000).fill('*');

在上述例子中,div对象的a属性引用了div对象自身,造成了最简单的循环引用,并且该属性并没有被移除或者显示设置为null,这对于引用计数器来说就是一个有意义的引用。因此,div对象会一直保持在内存中,即使在DOM树中将div1删除,而且div对象的someBigData所引用的数据也会一直保持在内存中,不会被释放。如果div对象本身很大,或者其属性引用的数据很大,那么持续累积就可能造成内存泄漏 。

标记清除

标记清除算法是对引用计数算法的改进,如果说引用计数算法是判断对象是否不再需要,那么标记清楚算法就是判断对象是否可以获得。可以获得的对象就保留在内存中,不可获得的对象就会被垃圾回收。

垃圾回收并不是实时的,使用标记清除算法的垃圾回收器,会定期从根对象开始,在js中就是从window对象开始,找出所有从根开始引用的对象,以及找到这些对象引用的对象,直到全部遍历。垃圾回收器就可以收集到所有可获得的对象以及所有不可获得的对象,然后将不可获得的对象回收。

使用标记清除算法可以有效解决引用计数算法的循环引用问题,还是刚刚那个例子:

function foo() {
    const obj = {name: '张三'};
    const obj2 = {age: 0};
    obj.a = obj2; // obj对象引用了obj2
    obj2.a = obj; // obj2引用了obj
}
foo();

foo()函数执行完毕后,垃圾回收器从window对象开始找,发现obj和obj2是不可获得的对象,那么其引用的数据就会被回收。我们简单修改一下代码:

function foo() {
    const obj = {name: '张三'};
    const obj2 = {age: 0};
    obj.a = obj2; // obj对象引用了obj2
    obj2.a = obj; // obj2引用了obj
    window.obj = obj;
}
foo();
console.log(window.obj)

foo()函数执行完毕,垃圾回收期从window对象开始找,发现可以在window对象上找到obj属性,obj和obj2存在循环引用,最终obj和obj2引用的数据都不会被垃圾回收。

这个例子也告诉我们,使用全局变量时一定要注意是否可能造成内存泄漏,详细可查看内存泄漏的场景。使用标记清楚算法就基本上能满足垃圾回收的需求了,而且从2012年开始,现代主流浏览器的垃圾回收器都实现了标记清除算法,后续的改进也是基于该算法来实现的。

闭包是内存泄漏吗

使用过闭包的都知道,其数据是保持在内存中的,不会被回收,那么闭包是内存泄漏吗?答案:闭包不是内存泄漏。因为我们说的内存泄漏是不符合预期的内存持续增长,闭包虽然也会占着内存不释放,但是这个是符合我们预期的效果。

闭包不是内存泄漏,难道就可以随便使用了吗。那肯定不是的,闭包中的数据如果很大,也会消耗大量的内存,造成网页卡顿,甚至崩溃。因此我们不能滥用闭包,在闭包函数退出前,一些不必要的局部变量该清除的还是要清除。

总结

本文详细的解释了什么是内存泄漏,泄漏了该怎么检测,浏览器是怎么来进行垃圾回收的,以及一些常见的内存泄漏场景。总的来说,内存泄漏就是指发生非预期的内存占用持续增长,以至于耗尽内存,导致系统崩溃。内存泄漏在实际开发过程中还是比较容易犯的,在写代码的时候一定要留意高发场景,尽可能在写代码的时候就避免潜在的内存泄漏,而不是等到系统崩溃了才来一句:重启(刷新)大法好。

以上就是一文搞懂JavaScript中的内存泄露的详细内容,更多关于JavaScript内存泄露的资料请关注我们其它相关文章!

(0)

相关推荐

  • 关于js内存泄露的一个好例子

    我把别人的例子改了一下,觉得这样写更紧凑!套用别人的原话,当一个DOM对象包含一个Js对象的引用(例如一个Event Handler), 而这个Js对象又持有对这个DOM对象的引用时,一个环状引用就行成了,于是在ie下就出现了内存泄露.点击"运行代码"并打开任务管理器看内存变化.分别在ie8和ff下测试,差距不用多说. 运行代码 复制代码 代码如下: <html>  <head>    <title>Memory leak</title>

  • js内存泄露的几种情况详细探讨

    内存泄露是指一块被分配的内存既不能使用,又不能回收,直到浏览器进程结束.在C++中,因为是手动管理内存,内存泄露是经常出现的事情.而现在流行的C#和Java等语言采用了自动垃圾回收方法管理内存,正常使用的情况下几乎不会发生内存泄露.浏览器中也是采用自动垃圾回收方法管理内存,但由于浏览器垃圾回收方法有bug,会产生内存泄露. 1.当页面中元素被移除或替换时,若元素绑定的事件仍没被移除,在IE中不会作出恰当处理,此时要先手工移除事件,不然会存在内存泄露. 复制代码 代码如下: <div id="

  • 解决JS内存泄露之js对象和dom对象互相引用问题

    该问题涉及到作用域链,js对象和dom对象互相引用的问题. 因为对于匿名函数而言,其作用域链包含三个对象:匿名函数的变量对象.doTry()的变量对象和全局变量对象.此时doTry函数对象中btn变量引用了dom对象,即doTry函数对象引用dom对象:              然后根据匿名函数包括的对象,dom对象的onclickà匿名函数对象.匿名函数对象àdoTry函数对象,即得dom对象引用doTry().所以解释了互相引用的情况存在,导致内存无法. 解决思路:将互相引用中断. 两种方

  • 权威JavaScript 中的内存泄露模式

    作者:Abhijeet Bhattacharya (abhbhatt@in.ibm.com), 系统软件工程师, IBM IndiaKiran Shivarama Shivarama Sundar (kisundar@in.ibm.com), 系统软件工程师, IBM India 2007 年 5 月 28 日 如果您知道内存泄漏的起因,那么在 JavaScript 中进行相应的防范就应该相当容易.在这篇文章中,作者 Kiran Sundar 和 Abhijeet Bhattacharya 将带

  • JS闭包、作用域链、垃圾回收、内存泄露相关知识小结

    补充: 闭包(closure)是Javascript语言的一个难点,也是它的特色,很多高级应用都要依靠闭包实现. 闭包的特性 闭包有三个特性: 1.函数嵌套函数 2.函数内部可以引用外部的参数和变量 3.参数和变量不会被垃圾回收机制回收 闭包的定义及其优缺点 闭包 是指有权访问另一个函数作用域中的变量的函数,创建闭包的最常见的方式就是在一个函数内创建另一个函数,通过另一个函数访问这个函数的局部变量 闭包的缺点就是常驻内存,会增大内存使用量,使用不当很容易造成内存泄露. 闭包是javascript

  • 一文搞懂JavaScript中的内存泄露

    目录 什么是内存泄漏 怎么检测内存泄漏 Performance Memory 内存泄漏的场景 垃圾回收算法 引用计数 循环引用 标记清除 闭包是内存泄漏吗 总结 以前我们说的内存泄漏,通常发生在后端,但是不代表前端就不会有内存泄漏.特别是当前端项目变得越来越复杂后,前端也逐渐称为内存泄漏的高发区.本文就带你认识一下Javascript的内存泄漏. 什么是内存泄漏 什么是内存?内存其实就是程序在运行时,系统为其分配的一块存储空间.每一块内存都有对应的生命周期: 内存分配:在声明变量.函数时,系统分

  • 一文搞懂JavaScript中原型与原型链

    目录 1.构造函数原型prototype 2.对象原型__proto__ 3.constructor构造函数 4.原型链 5.原型对象中的this指向 6.扩展内置对象(原型对象的应用) 在ES6之前,我们面向对象是通过构造函数实现的.我们把对象的公共属性和方法放在构造函数里 像这样: function student(uname,age) { this.uname = uname; this.age = age; this.school = function() { console.log('

  • 一文搞懂JavaScript中bind,apply,call的实现

    目录 bind.call和apply的用法 bind call&apply 实现bind 实现call和apply 总结 bind.call和apply都是Function原型链上面的方法,因此不管是使用function声明的函数,还是箭头函数都可以直接调用.这三个函数在使用时都可以改变this指向,本文就带你看看如何实现bind.call和apply. bind.call和apply的用法 bind bind()方法可以被函数对象调用,并返回一个新创建的函数. 语法: function.bin

  • 一文搞懂Golang中的内存逃逸

    目录 前言 什么是内存逃逸 查看对象是否发生逃逸 内存逃逸分析的意义 怎么避免内存逃逸 小结 前言 我们都知道go语言中内存管理工作都是由Go在底层完成的,这样我们可以不用过多的关注底层的内存问题,有更多的精力去关注业务逻辑, 但掌握内存的管理,理解内存分配机制,可以让你写出更高效的代码,本文主要总结一下 Golang内存逃逸分析,需要的朋友可以参考以下内容,希望对大家有帮助. 什么是内存逃逸 在了解什么是内存逃逸之前,我们先来了解两个概念,栈内存和堆内存. 堆内存(Heap):一般来讲是人为手

  • 一文带你搞懂JavaScript中的进制与进制转换

    目录 进制介绍 进制转换 parseInt(str, radix) Number() +(一元运算符) Number.prototype.toString(radix) 自定义转换 十进制与十六进制转换 十进制和二进制转换 进制介绍 JavaScript 中提供的进制表示方法有四种:十进制.二进制.十六进制.八进制. 对于数值字面量,主要使用不同的前缀来区分: 十进制(Decimal):取值数字 0-9:不用前缀. 二进制(Binary):取值数字 0 和 1 :前缀 0b 或 0B. 十六进制

  • 一文搞懂Spring中的注解与反射

    目录 前言 一.内置(常用)注解 1.1@Overrode 1.2@RequestMapping 1.3@RequestBody 1.4@GetMapping 1.5@PathVariable 1.6@RequestParam 1.7@ComponentScan 1.8@Component 1.9@Service 1.10@Repository 二.元注解 @Target @Retention @Documented @Inherited 三.自定义注解 四.反射机制概述 4.1动态语言与静态语

  • 一文搞懂Python中列表List和元组Tuple的使用

    目录 列表 List 列表是有序的 列表可以包含任意对象 通过索引访问列表元素 列表嵌套 列表可变 元组 Tuple 定义和使用元组 元素对比列表的优点 元组分配.打包和解包 List 与 Tuple 的区别 列表 List 列表是任意对象的集合,在 Python 中通过逗号分隔的对象序列括在方括号 ( [] ) 中 people_list = ['曹操', '曹丕', '甄姫', '蔡文姫'] print(people_list) ['曹操', '曹丕', '甄姫', '蔡文姫'] peopl

  • 一文搞懂Java中的注解和反射

    目录 1.注解(Annotation) 1.1 什么是注解(Annotation) 1.2 内置注解 1.3 元注解(meta-annotation) 1.4 自定义注解 2.反射(Reflection) 2.1 反射和反射机制 2.2 Class类的获取方式和常用方法 2.3 反射的使用 1.注解(Annotation) 1.1 什么是注解(Annotation) 注解不是程序本身,可以在程序编译.类加载和运行时被读取,并执行相应的处理.注解的格式为"@注释名(参数值)",可以附加在

  • 一文搞懂JavaScript如何实现图片懒加载

    目录 实现思路 准备知识 data-* getBoundingClientRect() throttle window.innerHeight 完整代码 js部分 CSS部分 运行结果 总结 图片懒加载,往往作为减少首页白屏时间的一个解决方案而出现.直观的来说,就是不要直接加载所有图片,而是满足一定条件后才加载,也就是”惰性加载“.实现图片懒加载的方式有很多,如果要简单点那就直接使用第三方插件:vue-lazyload,如果想探究一下别人的插件是怎么实现图片懒加载的,那么可以看看本文是如何实现的

  • 一文搞懂Java中对象池的实现

    目录 1. 什么是对象池 2. 为什么需要对象池 3. 对象池的实现 4. 开源的对象池工具 5. JedisPool 对象池实现分析 6. 对象池总结 最近在分析一个应用中的某个接口的耗时情况时,发现一个看起来极其普通的对象创建操作,竟然每次需要消耗 8ms 左右时间,分析后发现这个对象可以通过对象池模式进行优化,优化后此步耗时仅有 0.01ms,这篇文章介绍对象池相关知识. 1. 什么是对象池 池化并不是什么新鲜的技术,它更像一种软件设计模式,主要功能是缓存一组已经初始化的对象,以供随时可以

随机推荐