V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
推荐关注
Meteor
JSLint - a JavaScript code quality tool
jsFiddle
D3.js
WebStorm
推荐书目
JavaScript 权威指南第 5 版
Closure: The Definitive Guide
xiaoming1992
V2EX  ›  JavaScript

js 将大量图片保存在内存中会有问题吗?

  •  
  •   xiaoming1992 · 2019-12-18 10:28:21 +08:00 · 8138 次点击
    这是一个创建于 1804 天前的主题,其中的信息可能已经有所发展或是发生改变。

    较多的图片(预计 100 张共计 10-15M )需要随取随用,我打算将他们保存在内存中,

    const imgs: Image[] = []
    

    不知道会不会对程序的运行有什么不好的影响?比方说卡顿啊、图片内容丢失啊之类的?能推荐这方面(我也不知道哪方面...)的科普文就更好了。

    第 1 条附言  ·  2019-12-18 11:41:58 +08:00

    有些同学对我说的“保存在内存”有疑问,直接上我的很简陋的方法

    export function fetchImg(url: string) {
      return new Promise<HTMLImageElement>((resolve, reject) => {
        const img = new Image()
        function onload() {
          img.removeEventListener("load", onload)
          // eslint-disable-next-line @typescript-eslint/no-use-before-define
          img.removeEventListener("error", onerror)
          resolve(img)
        }
        function onerror(e: ErrorEvent) {
          img.removeEventListener("load", onload)
          img.removeEventListener("error", onerror)
          reject(e)
        }
        img.addEventListener("load", onload)
        img.addEventListener("error", onerror)
        img.src = url
      })
    }
    
    第 2 条附言  ·  2019-12-18 12:24:01 +08:00
    业务需求其实是一个环物展示,不过有些特殊,会像 ps 图层一样有好几层,我设想的是,加载出所需的几张图片后,利用 ctx.drawImage 依次将他们画到 canvas 上。将所需的 360 度的图片全部加载后保存在数组中,根据动作显示不同的图片。
    第 3 条附言  ·  2019-12-18 15:01:59 +08:00
    经过查看大家的回复,我发现可能是我搞错了点东西,我这儿的操作应该是,图片利用浏览器缓存(而非内存),将这些图片用一个数组装着,随用随取,避免浏览器自己清了缓存。
    53 条回复    2019-12-19 20:31:35 +08:00
    Foreverdxa
        1
    Foreverdxa  
       2019-12-18 10:57:43 +08:00   ❤️ 1
    没有影响,放在内存速度只会更快。你硬件到位,内存随便用,但是你应该明白内存跟 磁盘的区别吧。
    Eempty
        2
    Eempty  
       2019-12-18 11:02:53 +08:00   ❤️ 1
    现在的浏览器下,一般情况下没问题的,内存基本都够用
    nvkou
        3
    nvkou  
       2019-12-18 11:04:32 +08:00 via Android   ❤️ 1
    好歹放 local storage 吧
    neoblackcap
        4
    neoblackcap  
       2019-12-18 11:23:59 +08:00   ❤️ 1
    淘宝就是将很多图片放内存的,没有问题,前提是你的程序不会爆内存
    noobma
        5
    noobma  
       2019-12-18 11:26:42 +08:00   ❤️ 1
    window.performance.memory.jsHeapSizeLimit
    应该足够你用的
    Torpedo
        6
    Torpedo  
       2019-12-18 11:27:54 +08:00   ❤️ 1
    这种应该不是直接存在了内存里吧。
    就和你页面加载了 100 张图片,js 里肯定只是对这些资源的引用。
    具体你通过 html 加载图片还是 js 都是一样的
    7654
        7
    7654  
       2019-12-18 11:29:00 +08:00   ❤️ 1
    对于现在浏览器动辄几个 G 的内存占用,这点不算什么 doge
    noobma
        8
    noobma  
       2019-12-18 11:29:55 +08:00   ❤️ 1
    @Torpedo 楼主的意思应该是直接存 blob
    royzxq
        9
    royzxq  
       2019-12-18 11:31:32 +08:00   ❤️ 1
    首选考虑的应该是你需要多少时间来把这上 G 的图片加载进内存里。
    xiaoming1992
        10
    xiaoming1992  
    OP
       2019-12-18 11:36:03 +08:00
    @Foreverdxa 只是我不知道操作系统 /运行环境会不会对单个页面可用内存做限制,比方说虽然我手机内存 6G,但是手机只分配给微信 1G,微信只分配给单个 html 页面 100M,数值是随便说的,意思到位就行。

    @nvkou 图片取用 /增删会很频繁,比方说 10 次 /s,这些图片说多不多,说少不少的,不太想放到 localStorage 里面,还得写兼容杂七杂八的,如果没大问题就直接放内存了,毕竟我看 three.js 也是直接保存在内存的
    belin520
        11
    belin520  
       2019-12-18 11:38:39 +08:00   ❤️ 1
    Chrome 单个 tab 有内存使用上限,不多的话,没有问题的。
    xiaoming1992
        12
    xiaoming1992  
    OP
       2019-12-18 11:38:54 +08:00
    @noobma 感谢,很有用!

    @noobma 不是啊,我描述里面有,直接就是简单的 `const imgs: Image[] = []` 。。。
    zhw2590582
        13
    zhw2590582  
       2019-12-18 11:39:18 +08:00   ❤️ 1
    15M 不是事,我存视频数据到内存都是几个 G 的。
    xiaoming1992
        14
    xiaoming1992  
    OP
       2019-12-18 11:42:24 +08:00
    @royzxq 总共 10-15M,不是单张。。。
    royzxq
        15
    royzxq  
       2019-12-18 11:48:36 +08:00   ❤️ 1
    @xiaoming1992 哦抱歉抱歉没看仔细,那才这么点大一点事情没有
    xiangyuecn
        16
    xiangyuecn  
       2019-12-18 12:00:35 +08:00   ❤️ 1
    看你#10 楼说的,这么大的时间间隙,最佳还是按需实时从网络加载更好些,全部缓存到内存是否有提前优化的嫌疑?

    另外猜测意图并非同时需要显示大量图片,如果真需要全部缓存到内存时,尽量不要用 Image 对象来加载图片,自己写 xhr 请求得到二进制 ArrayBuffer,最多内存占用也就比 15M 多点,按需实时从内存实例化 Image 图片对象( onload 非常快)。直接用 Image 来进行缓存内存占用会翻个 10 几倍也是正常。
    weixiangzhe
        17
    weixiangzhe  
       2019-12-18 12:14:04 +08:00 via Android   ❤️ 1
    为什麽要保存在内存中呢,图片用 new image 加载一次回就有缓存了,之后直接用 src 就好了
    xiaoming1992
        18
    xiaoming1992  
    OP
       2019-12-18 12:17:00 +08:00 via Android
    @xiangyuecn 业务需求实际是同一时刻只有 3-7 张图片,然而需要根据鼠标动作频繁切换这几张图片的 src,再讲细一点就是比较特殊的环物展示。我还没想过直接处理 buffer,回去考虑一下,谢谢。
    xiaoming1992
        19
    xiaoming1992  
    OP
       2019-12-18 12:27:12 +08:00 via Android
    @weixiangzhe 其实本来就是利用的浏览器的缓存,或许是我描述有误
    maichael
        20
    maichael  
       2019-12-18 12:32:43 +08:00   ❤️ 1
    实测一下不就知道了。
    lamada
        21
    lamada  
       2019-12-18 12:38:27 +08:00   ❤️ 1
    缓存一般没有问题,楼上说的爆内存和这个关系应该不大。移动端图片爆内存的情况是一般是图片尺寸的原因,和文件大小无关。你可以计算所有展示的图片 w*h*4 来估算一下渲染时所占用的内存。
    xiaoming1992
        22
    xiaoming1992  
    OP
       2019-12-18 12:44:40 +08:00
    @lamada 我之前用过同时显示 8 张 4096*2048 的图片,手机动不动就黑屏……现在仅仅是 3-6 张 2000*1000,这么算的话大概百来 M,应该问题不大。
    xiaoming1992
        23
    xiaoming1992  
    OP
       2019-12-18 12:47:49 +08:00
    @maichael 设备太多了,没法一一测试啊,就怕内存占用处在临界点,这台设备好用,那台设备崩了,或者由于不同设备的缓存策略不同,缓存太多,如果动不动给我自动清掉一些缓存,也很烦啊。
    weixiangzhe
        24
    weixiangzhe  
       2019-12-18 14:41:21 +08:00 via Android   ❤️ 1
    图片太大结合 oss 的图片处理用,可以压缩为 webp 的格式 再控制下长宽就会好很多
    mxT52CRuqR6o5
        25
    mxT52CRuqR6o5  
       2019-12-18 14:43:22 +08:00 via Android   ❤️ 1
    没啥问题,渲染出来的图片才会影响性能
    xiaoming1992
        26
    xiaoming1992  
    OP
       2019-12-18 14:50:24 +08:00
    @weixiangzhe 大小不能压缩,还要支持放大了看也清楚,已经压缩的足够了,2000*1000 的图片现在已经 100k 不到了
    ofblyt
        27
    ofblyt  
       2019-12-18 14:52:17 +08:00   ❤️ 1
    display:none 的图片好像也可以用在 canvas 里面,所以其实加几个隐藏的 div 加载图片就行
    mxT52CRuqR6o5
        28
    mxT52CRuqR6o5  
       2019-12-18 14:55:35 +08:00 via Android   ❤️ 1
    @mxT52CRuqR6o5 不过楼主这种增加抽象复杂度的随取随用也没啥用,随便找个预加载库就是了
    xiaoming1992
        29
    xiaoming1992  
    OP
       2019-12-18 14:56:47 +08:00
    @mxT52CRuqR6o5 不太清楚"增加抽象复杂度"是什么意思,是说我这么搞让问题更复杂了嘛?
    mxT52CRuqR6o5
        30
    mxT52CRuqR6o5  
       2019-12-18 15:01:48 +08:00 via Android   ❤️ 1
    @xiaoming1992 比如常规方法是通过图片 url 现场 new Image 插入网页,哪个 Image 是哪个 url 对应关系都很清楚,你先把所有的 Image 预生成再存到一个数组里,(别人接手项目)可能就不能马上理清每个 Image 对应哪个图片
    xiaoming1992
        31
    xiaoming1992  
    OP
       2019-12-18 15:06:32 +08:00
    @mxT52CRuqR6o5 由于我这边时不时需要操作这一组有序的图片,这一组 url 将会按照一定的规律画到同一个 canvas 上,所以才将他们放在一个数组里面的。。。
    CODEWEA
        32
    CODEWEA  
       2019-12-18 15:09:31 +08:00   ❤️ 1
    道理我都懂,可是你这个就顶多是个图片懒加载啊,你如何证明是保存到 js 了?
    mxT52CRuqR6o5
        33
    mxT52CRuqR6o5  
       2019-12-18 15:12:43 +08:00 via Android   ❤️ 1
    @xiaoming1992 url 现场生成 image 再画到 canvas 上,中间的步骤可以当做黑箱不用去太关心,更符合直觉
    mxT52CRuqR6o5
        34
    mxT52CRuqR6o5  
       2019-12-18 15:16:54 +08:00 via Android   ❤️ 1
    @mxT52CRuqR6o5 更符合直觉指的是我输入了一个 url,在 canvas 上就渲染了对应的图片
    xiaoming1992
        35
    xiaoming1992  
    OP
       2019-12-18 15:22:05 +08:00
    @mxT52CRuqR6o5 对,但是我必须预加载他,不能我需要了再临时加载,这是我做的一个小 demo https://m.lmoar.com/vrs/t/dist-example/example.html

    @CODEWEA 对,是图片预加载,如果仅仅 new Image.src = url,那么浏览器很快就会清掉缓存,我这边将返回的 img 保存到数组里面,**或许 /应该 /可能**浏览器不会清掉缓存。
    xiaoming1992
        36
    xiaoming1992  
    OP
       2019-12-18 15:29:29 +08:00
    @xiangyuecn 回来看了一下,ctx.drawImage 好像不能接受 blob 作为参数。。。
    mxT52CRuqR6o5
        37
    mxT52CRuqR6o5  
       2019-12-18 15:32:18 +08:00 via Android   ❤️ 1
    @xiaoming1992 随便找个预加载的库就是了
    sneezry
        38
    sneezry  
       2019-12-18 15:38:21 +08:00 via iPhone   ❤️ 1
    @nvkou localStorage 只能放字符串,并且单个域名在多数浏览器里有 5M 的限制
    xiangyuecn
        39
    xiangyuecn  
       2019-12-18 15:39:40 +08:00   ❤️ 1
    @xiaoming1992 #36 URL.createObjectURL(blob)
    xiaoming1992
        40
    xiaoming1992  
    OP
       2019-12-18 15:43:45 +08:00
    @mxT52CRuqR6o5 我好像确实忘了去找库了,其实我一直纠结的是,浏览器会不会把我预加载的东西给清理掉
    Mutoo
        41
    Mutoo  
       2019-12-18 15:46:26 +08:00   ❤️ 1
    游戏引擎就是这么做的,没什么问题。
    jsq2627
        42
    jsq2627  
       2019-12-18 19:29:33 +08:00   ❤️ 2
    https://codepen.io/jsq2627/pen/ZEYLyEW?editors=1010

    不做分片加载的话,那就有很大概率内存占用超出上限,页面崩溃。特别是低端安卓机。
    如果你能手动控制好分片加载的话,那自然是 ok 的。
    另外存 blob 和存 Image 有个区别是,blob 占用的是 JS heap 内存,最大上限比存 Image 小很多。
    xiaojie668329
        43
    xiaojie668329  
       2019-12-18 21:30:07 +08:00 via iPhone   ❤️ 1
    100 张肯定没问题,我试过存过千张几百 kb 的。
    baihaihui
        44
    baihaihui  
       2019-12-18 21:59:45 +08:00   ❤️ 1
    根据实现,图片不一定在内存。具体是 from disk cache 还是 from memory cahe 应该是分情况的
    xiangyuecn
        45
    xiangyuecn  
       2019-12-18 23:21:18 +08:00   ❤️ 2
    @jsq2627 #42 这段代码有意思。我试了一个 200kb 的图片( 1800*1200 )加载 200 次的内存占用(约 50M 流量)



    任务管理器里面报的内存,不知道是 windows 的是实际的还是 chrome 的是实际的,不过应该 600M+是有的,performance 里面的值雷打不动😂

    刚开始加载并渲染 和 只加载不渲染 内存占用差不多( 50M 左右增量),然后我拖动页面让所有的图片都至少可见一次,内存立马爆炸到 600 多 M ;移除所有已显示的图片后,手动 GC 后内存正常回收。

    看样子 Image 的性能优化的确实厉害,缓存了 Image 就没有必要用 Blob 了。

    -------

    另外还测试了一下 2M 的 Blob *200 个,内存占用出现在浏览器的主进程( ID:8612 )上,并不在网页窗口的进程上;测试不断创建 1M 的 ArrayBuffer 就能把 chrome 主进程的内存占光(任务管理器里面看 2.4G 左右就不动了),然后内存放不下的 blob 疯狂写入硬盘( User Data\Default\blob_storage 目录),目测会把硬盘写满😂。测试代码( 12G 数据,机械盘估计更明显):
    ```
    s=[];for(var i=0;i<12000;i++)s.push(new Blob([new Uint8Array(1*1024*1024)]));console.log("end")
    ```
    xiaoming1992
        46
    xiaoming1992  
    OP
       2019-12-18 23:49:31 +08:00
    @jsq2627 @baihaihui @xiangyuecn 这样说来,将图片直接用 image 缓存应该是可行的,不用做其他处理了吧。

    梳理了一下自己的思路之后,发现我最初的疑问应当是,“浏览器是否会在一定的时候清理已缓存的图片”,经过初步测试,至少保存在数组中的 image 不会被清理。
    jsq2627
        47
    jsq2627  
       2019-12-19 00:14:00 +08:00
    @xiangyuecn #45 看来我上面说的也不太对,blob 不占用 js heap,是有专门的存储系统来处理的
    https://chromium.googlesource.com/chromium/src/+/master/storage/browser/blob/README.md


    @xiaoming1992 #46 应该只要注意内存量级就行。内存占用太多的话,低端设备会频繁触发 memory swapping,反而变成负优化
    KuroNekoFan
        48
    KuroNekoFan  
       2019-12-19 11:44:42 +08:00
    new Image 然后设置 src 不就行了
    让浏览器自己管理 cache 不好吗,hack 除了好玩到底有啥优势
    baihaihui
        49
    baihaihui  
       2019-12-19 13:16:30 +08:00
    @xiaoming1992 浏览器是否清除缓存取决与缓存是如何配置的。有强缓存,协商缓存。对于图片直接配置成强缓存就行。如果内存使用过高,浏览器会自动落盘( from disk cache )。
    muzuiget
        50
    muzuiget  
       2019-12-19 15:00:03 +08:00
    楼主想多了,这种情况让浏览器自己优化就好了。说不定图片不可见,浏览器自己会把图片内存交换到硬盘上去呢。
    xcstream
        51
    xcstream  
       2019-12-19 15:12:35 +08:00
    直接引用地址浏览器自动缓存的。
    xiaoming1992
        52
    xiaoming1992  
    OP
       2019-12-19 19:56:17 +08:00 via Android
    @KuroNekoFan 仅仅是 new image src,浏览器很快就会清掉缓存,所以才把他们存数组里的。
    KuroNekoFan
        53
    KuroNekoFan  
       2019-12-19 20:31:35 +08:00 via iPhone
    @xiaoming1992 浏览器缓存是被 http header 控制的,请补充相关知识……
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   2356 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 28ms · UTC 16:09 · PVG 00:09 · LAX 08:09 · JFK 11:09
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.