js 将大量图片保存在内存中会有问题吗? - V2EX
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 日 8665 次点击
    这是一个创建于 2226 天前的主题,其中的信息可能已经有所发展或是发生改变。

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

    const imgs: Image[] = [] 

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

    第 1 条附言    2019 年 12 月 18 日

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

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

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

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

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

    不做分片加载的话,那就有很大概率内存占用超出上限,页面崩溃。特别是低端安卓机。
    如果你能手动控制好分片加载的话,那自然是 ok 的。
    另外存 blob 和存 Image 有个区别是,blob 占用的是 JS heap 内存,最大上限比存 Image 小很多。
    xiaojie668329
        43
    xiaojie668329  
       2019 年 12 月 18 日 via iPhone   1
    100 张肯定没问题,我试过存过千张几百 kb 的。
    baihaihui
        44
    baihaihui  
       2019 年 12 月 18 日   1
    根据实现,图片不一定在内存。具体是 from disk cache 还是 from memory cahe 应该是分情况的
    xiangyuecn
        45
    xiangyuecn  
       2019 年 12 月 18 日   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 日
    @jsq2627 @baihaihui @xiangyuecn 这样说来,将图片直接用 image 缓存应该是可行的,不用做其他处理了吧。

    梳理了一下自己的思路之后,发现我最初的疑问应当是,“浏览器是否会在一定的时候清理已缓存的图片”,经过初步测试,至少保存在数组中的 image 不会被清理。
    jsq2627
        47
    jsq2627  
       2019 年 12 月 19 日
    @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 日
    new Image 然后设置 src 不就行了
    让浏览器自己管理 cache 不好吗,hack 除了好玩到底有啥优势
    baihaihui
        49
    baihaihui  
       2019 年 12 月 19 日
    @xiaoming1992 浏览器是否清除缓存取决与缓存是如何配置的。有强缓存,协商缓存。对于图片直接配置成强缓存就行。如果内存使用过高,浏览器会自动落盘( from disk cache )。
    muzuiget
        50
    muzuiget  
       2019 年 12 月 19 日
    楼主想多了,这种情况让浏览器自己优化就好了。说不定图片不可见,浏览器自己会把图片内存交换到硬盘上去呢。
    xcstream
        51
    xcstream  
       2019 年 12 月 19 日
    直接引用地址浏览器自动缓存的。
    xiaoming1992
        52
    xiaoming1992  
    OP
       2019 年 12 月 19 日 via Android
    @KuroNekoFan 仅仅是 new image src,浏览器很快就会清掉缓存,所以才把他们存数组里的。
    KuroNekoFan
        53
    KuroNekoFan  
       2019 年 12 月 19 日 via iPhone
    @xiaoming1992 浏览器缓存是被 http header 控制的,请补充相关知识……
    关于     帮助文档     自助推广系统     博客     API     FAQ       3065 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 34ms UTC 13:58 PVG 21:58 LAX 05:58 JFK 08:58
    Do have faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86