壹影博客.
我在下午4点钟开始想你
AndroLua 内存优化详细介绍及经验分享
  • 2023-11-5日
  • 1评论
  • 5963围观

AndroLua 内存优化详细介绍及经验分享

前言:在文章开始之前,看看你是否能很好的回答如下问题,如下问题由浅入深

1.是否了解AndroLua开发中实际上用了哪些主要的API?
2.AndroLua应用程序开了多少虚拟机?
3.如何获取虚拟机的内存信息?
4.是否了解GC的垃圾回收机制?
5.你了解建议GC和主动GC的区别么?
6.你知道System.GC() 和 Runtime.GC() 有什么区别么?
7.如何解决处理OutOfMemoryError内存溢出的问题?

本篇文章将为从如上问题入手带你深入了解AndroLua 内存优化问题

1、你是否了解AndroLua开发中实际上用了哪些主要的API?

这个问题比较简单也比较开放,不同的人有不同的回答,不过我认为AndroLua主要是基于Lua语言使用Andrioid API的一套开发的路径,毋庸置疑,这玩意Lua自己的API得要用吧(Lua5.3),Android这边又是基于Java的API搞出来的一套,那么也就是说,Lua只需要提供一个实现Java的接口即可,并且这个接口是在C上去实现的,何以见得?你可以随便打包一个AndroLua的Apk解压后你会发发现里面一定会有so的动态库(libluajava.so),你甚至可以反编译他

这样看来AndroLua开发 其实还是要学习很多东西 你至少得会Lua的基本语法、Android基本布局、JavaSE的基础知识做铺垫才行,会一些基础知识才能更好的优化你的项目。

答:Lua 和 Android、Java

2、你知道AndroLua应用程序开了多少JVM么?

其实这个问题应该这么问,应该换成AndroLua开发中Lua和Java是否用的是一个JVM呢?我们只有明白了这个问题才能对JVM虚拟机做内存优化啊,其实从第一个问题我们就很明显的感觉到 Lua是一块、Java是一块 。反问你一句Lua能直接调取Java变量么?Java能直接调用Lua的变量么?

哈哈,显然是不可以,由此可见 Lua与 Java他们的变量是分别存储的用的不是一个JVM虚拟机啊

答:至少得有两个吧 Lua 和 Java

3、你知道如何获取虚拟机的内存信息么?

这个问题豁然开朗~ 你都知道了他们两个是不同的东西 那么获取JVM剩余内存也有两套咯~

先说Lua阵营:lua(5.3) 中可以用自带的collectgarbage函数 提供count字符串参数来获取已使用的内存数,返回K字节数,代码如下

-- 获取当前Lua虚拟机已用的内存大小
local num = collectgarbage("count")
print("已使用:"..num.."KB")

-- 在空间中声明50万个变量
for i=1,500000 do
	loadstring("index"..i.."="..i)()
end

--再次获取Lua虚拟机已用的内存大小
local num = collectgarbage("count")
print("已使用:"..num.."KB")

-- 我这边用模拟器测试至少定义500万个变量
-- 需占用 684.390M

Lua里面进行垃圾清理可利用collectgarbage() 不带任何参数 来进行清理

Java阵营:可用使用java.lang.Runtime包下的Runtime.freeMemory()来获取剩余内存数返回字节数但是需要注意的是利用Runtime.freeMemory()返回的数/1024 才是真正的字节数(Kb),代码如下

local JavaRuntime = luajava.bindClass "java.lang.Runtime"
local Runtime = JavaRuntime.getRuntime() --获取Runtime对象
print("JVM空闲内存:"..string.format("%.2f",((Runtime.freeMemory()/1024/1024).."")).."M")

Java里面进行垃圾清理的方法一般来说有两个第一个就是java.lang.Runtime包下的gc()方法 另一个则是java.lang.System包下的gc() 他们都可以进行垃圾收集 具体有什么不同 见后面的问题....

我们可以利用如下代码来验证这个Lua和Java他们是不是一个虚拟机 哈哈 免得有人不信

local JavaRuntime = luajava.bindClass "java.lang.Runtime"

-- 获取当前的Java虚拟机信息对象
local Runtime = JavaRuntime.getRuntime() 
print("JVM空闲内存:"..string.format("%.2f",((Runtime.freeMemory()/1024/1024).."")).."M")

--然后我们定义50万个变量在lua里面
--看看Java那边有没有什么变化
for i=1,500000 do
	loadstring("index"..i.."="..i)()
end

-- 再次获取当前的Java虚拟机信息对象
local Runtime = JavaRuntime.getRuntime() 
print("JVM空闲内存:"..string.format("%.2f",((Runtime.freeMemory()/1024/1024).."")).."M")

 你会发现两次打印结果是一样的

4、如何进行垃圾收集处理(GC)?

我这里是多插入了一个问题 害怕有些老铁不会手动GC 贴一段代码老铁自己看哦

-- 手动触发Lua虚拟机垃圾收集器(GC)
for i=1,500000 do
	loadstring("index"..i.."="..i)()
end
local front = collectgarbage("count")
collectgarbage()
local after = collectgarbage("count")
print("Lua处理前(已用):"..front.."Kb")
print("Lua处理后(已用):"..after.."Kb")


-- 手动触发Java虚拟机垃圾收集器(GC)
-- AndroLua 将Java的对象交给了Android进行管理 所以 你打开app的时候
-- 自动就会创建不少的对象 直接打印清理前后的结果即可
local JavaRuntime = luajava.bindClass "java.lang.Runtime"

-- 获取当前的Java虚拟机信息对象
local Runtime = JavaRuntime.getRuntime() 
print("Java清理前(剩余):"..string.format("%.2f",((Runtime.freeMemory()/1024/1024).."")).."M")

-- 手动触发Java虚拟机垃圾收集器(GC)
Runtime.gc() 

-- 再次获取当前的Java虚拟机信息对象
local Runtime = JavaRuntime.getRuntime() 
print("Java清理后(剩余):"..string.format("%.2f",((Runtime.freeMemory()/1024/1024).."")).."M")

查看输出结果:

5.你了解GC的垃圾回收机制么?

深入的就不谈了,只说实际的,GC垃圾清除的背后是有一堆算法的 这个玩意是Java面试的时候经常问的一个问题 你了解垃圾收集器吗,垃圾收集算法呢?简单说一下大概有如下几种算法标记清除算法、复制算法、标记整理算法、分代收集算法等等 ,每一种都有自己的特点 ,当然还有一些收集器等 这些都不是重点 ,重点是与我们开发比较近的就是这个GC是如何确定你的对象是一个垃圾的,这个玩意不限于语言Lua和Java的确定垃圾的原理几乎是差不多的,主要分两种


①引用计数法
一个对象如果没有任何与之关联的引用,即他们的引用计数都不为0,则说明对象不太可能再被用到,那么这个对象就是可回收对象。

②可达性分析
通过一系列的“GC roots”对象作为起点搜索。如果在“GC roots”和一个对象之间没有可达路径,则称该对象是不可达的。要注意的是,不可达对象不等价于可回收对象,不可达对象变为可回收对象至少要经过两次标记过程。两次标记后仍然是可回收对象,则将面临回收。

 

6.你了解建议GC和主动GC的区别么?

这个就是收集器机制问题了,当一个变量 或者是对象成为了垃圾之后 系统并不是直接把他干掉,不同的虚拟机的触发是不一样的,比如内存使用率达到百分之多少的时候会主动触发GC 或者是其他的触发条件,总之明白这件事情,如果一个对象没有用的时候 系统不是立刻把他清除掉,而是过一段时间之后,所以这个建议GC和主动GC就是这样一个区别,主动GC是立刻清除掉垃圾 而 建议GC他不是立刻的,当前建议GC还是会清除一部分内容的但是没有主动GC清除的多而已。

Lua垃圾收集器没有让你设置主动还是被动 哈哈 所以你只要调用lcollectgarbage() 就是立刻马上!

Java就不一样了由于Java底层搞了很多的机制内部对象是比较复杂的比如双清委派模型等 ,在Java里面

System.gc()  是建议GC
Runtime.gc() 是主动GC

其实这个也是System.gc()和Runtime.gc()的区别 ,那么我们来验证一下吧

local JavaRuntime = luajava.bindClass "java.lang.Runtime"
local System = luajava.bindClass "java.lang.System"

--先封装一个函数用于获取 当前Java虚拟机的剩余内存
function printMemory(str)
	-- 获取当前的Java虚拟机信息对象
	local Runtime = JavaRuntime.getRuntime() 
	print(str..string.format("%.2f",((Runtime.freeMemory()/1024/1024).."")).."M")
end

printMemory("Java建议GC前(剩余):")
-- 我们触发一次建议GC
System.gc() 
printMemory("Java建议GC后(剩余):")


--为了打印方便我这里就再重复上面的一句代码方便大家更好的理解
printMemory("Java主动GC前(剩余):")
local Runtime = JavaRuntime.getRuntime() 
Runtime.gc() 
printMemory("Java主动GC后(剩余):")

查看结果

7.你知道System.GC() 和 Runtime.GC() 有什么区别么?

其实就是第6点讲的建议GC和主动GC的区别这里就不再赘述了...见上文

8.如何解决处理OutOfMemoryError内存溢出的问题?

首先你应该明白你看到错误的时候其实是Java级别的内存溢出 OutOfMemoryError,所以你要处理的是Java对象并不是Lua对象

当你明白原理之后,你应该不会有这个内存溢出如何解决的这个问题了,你回想一下平时写AndroLua的时候有没有添加很多布局,将布局写到对话框里面的时候关闭对话框的这些布局对象会马上的清除么?在代码中无用的适配器有没有主动的清除 清除后有没有主动的调用GC?虽然在Android中 基础布局会为你做很多优化,但是很多变量由于我们使用不当依然是没有清除的,如果你遇到是布局导致的内存溢出的问题 ,建议先看看你的lua里面适配器的优化还在么,比如先使用主动GC去清除一次 调用一次Runtime.GC() 等等.... 我这里在下面贴如下几点经验,供大家参考学习,如果大家有自己的优化经验也欢迎分享,共同进步...

1.先看看你的代码中如果使用了System.GC()的地方有没有真的清除掉了无用的数据,如果没有的话可以尝试替换为Runtime.GC()

2.复杂布局的适配器在不用的时候 手动设nil 并且手动调用Runtime.GC() 以及collectgarbage()  双清(Lua和Java都做一次)只要没有对象去引用变量了就会被清理掉

3.在对话框内做复杂布局的时候可以设置.setOnDismissListener 对话框关闭监听 关闭之后去主动释放内存

4.关于WebView的优化 如果是多个浏览器 协同操作的时候要为不用的浏览器设空 主动释放虚拟机内存,对于浏览器缓存的处理我后面会单独发一篇文章来讲解

其实主要是做到这几点就能够解决大部分的问题,

文章编写不易,在自己学习的同时也可以将干货内容输出给到大家,希望大家持续关注我的博客,如果大家有什么自己的想法也欢迎在评论区讨论,或者您有什么干货教程也可以联系到我,我负责编写到博客分享出来(作者QQ:203455278),今天的分享就到这里,我们下期再见~

作者:壹影
链接:https://bk.yyge.net/?post=190
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

 

 

发表评论

WilliambrAsp

Lv.1 @回复 沙发

水微晶玻尿酸 - 八千代


https://yachiyo.com.tw/hyadermissmile-injection/

渝ICP备19011465号 | 渝ICP备19011465号-1