设为首页收藏本站|繁體中文

Project1

 找回密码
 注册会员
搜索
查看: 3827|回复: 6
打印 上一主题 下一主题

[RMVA发布] 【元编程的小小应用】追踪未释放的对象

[复制链接]

Lv3.寻梦者 (版主)

…あたしは天使なんかじゃないわ

梦石
0
星屑
2208
在线时间
4033 小时
注册时间
2010-10-4
帖子
10779

开拓者贵宾

跳转到指定楼层
1
发表于 2014-8-2 19:46:29 | 只看该作者 回帖奖励 |正序浏览 |阅读模式

加入我们,或者,欢迎回来。

您需要 登录 才可以下载或查看,没有帐号?注册会员

x
本帖最后由 taroxd 于 2014-8-2 20:03 编辑

写这个东西的原因,是某游戏制作组表示内存溢出,然后我就帮他们检查了一下。
一检查吓一跳,原来著名的 Yanfly Battle Engine 还有 Fuki 对话框都有未释放 Sprite 或者 Bitmap 的问题!

我想说的,是这些 Ruby 的技巧展现出的威力。
@喵呜喵5 和我说,制作一个游戏不需要超过 F1 手册的 Ruby 知识。
但我觉得,对 Ruby 理解的更深入一点总是好的。
至少,不用这一种【内省】的方式,是很难找出别人写的代码中的 bug 的。

代码中的 def_xxx 可以理解为我的免 alias 补丁(也是元编程!),也可以简单地理解为伪代码,表示在原方法前后补充执行一段代码。
请把下面的脚本当作一个学习脚本的示例,而不是把这个脚本直接拿去用。因为不会脚本的人,你用了也看不懂输出结果。

RUBY 代码复制
  1. class Viewport
  2.   def_before(:dispose) { @__dispose__ = true }
  3.   def disposed?; @__dispose__; end
  4. end
  5.  
  6. need_dispose = [Bitmap, Sprite, Window, Plane, Tilemap, Viewport]
  7. callers = {}
  8. callers.compare_by_identity
  9. not_disposed = []
  10.  
  11. need_dispose.each do |klass|
  12.   klass.class_eval do
  13.     def_after(:initialize) {|*| callers[self] = caller }
  14.   end
  15. end
  16.  
  17. Scene_Base.class_eval do
  18.   def_after :terminate do
  19.     need_dispose.each do |klass|
  20.       ObjectSpace.each_object(klass) do |obj|
  21.         not_disposed.push(obj) unless obj.disposed?
  22.       end
  23.     end
  24.   end
  25.  
  26.   def_after :update do
  27.     return unless Input.trigger?(:ALT) # 按下ALT键时显示检测结果
  28.     puts not_disposed.delete_if(&:disposed?) # 输出所有未释放的对象
  29.     puts callers[
  30.       not_disposed.shuffle.find do |obj|
  31.         # 随机找出一个不是 Cache 中生成的,未释放的对象,输出其 caller
  32.         # 如果不存在,则输出一个空行
  33.         callers[obj] && callers[obj].none? {|str| str.start_with?('{0004}') }
  34.       end
  35.     ]
  36.   end
  37. end


Q: 找到未释放的对象之后,为什么不直接释放该对象,而是追踪该对象的生成呢?
A: 我希望从脚本的根源解决问题,而不是掩盖一个脚本设计者的疏忽。

点评

不早说……  发表于 2014-8-2 21:08
当然,那个 def_after 和 def_before 你懂的  发表于 2014-8-2 21:04
这个脚本可以直接用,我就是直接用的。  发表于 2014-8-2 21:04
我的想法:脚本最好既可以当作一个学习脚本的示例,也可以把这个脚本直接拿去用  发表于 2014-8-2 21:03
比如说 http://rm.66rpg.com/thread-360213-1-1.html ,虽然不是VA,也仅仅释放了Bitmap  发表于 2014-8-2 20:49

评分

参与人数 1星屑 +12 收起 理由
3106345123 + 12 精品文章

查看全部评分

Lv5.捕梦者 (暗夜天使)

只有笨蛋才会看到

梦石
1
星屑
21681
在线时间
9419 小时
注册时间
2012-6-19
帖子
7119

开拓者短篇九导演组冠军

7
发表于 2014-9-22 12:40:25 | 只看该作者
仔细一想viewport和spirit出现原因一样,都是测试时用的代码不遵守自己的规范造成的,仍然和我自己正式的代码毫无关系...

点评

总之我只是证明一下这个脚本还是有点用的~  发表于 2014-9-22 12:43
spirit -> sprite  发表于 2014-9-22 12:43
回复 支持 反对

使用道具 举报

Lv5.捕梦者 (暗夜天使)

只有笨蛋才会看到

梦石
1
星屑
21681
在线时间
9419 小时
注册时间
2012-6-19
帖子
7119

开拓者短篇九导演组冠军

6
发表于 2014-9-21 22:22:38 | 只看该作者
本帖最后由 喵呜喵5 于 2014-9-21 22:52 编辑

为什么 p @viewport.class.instance_methods 的结果里找不到 disposed? 方法……



P.S

怀着喜悦的心情把Sprite的问题解决了,原来是测试用代码里手滑多写了一个super……

点评

所以我在前四行加上了啊~ 解决就好。 那些 Bitmap 是缓存里的,不用管;但是 Viewport 你还是检查一下吧。两个我都告诉你了  发表于 2014-9-22 12:27
回复 支持 反对

使用道具 举报

Lv3.寻梦者 (版主)

…あたしは天使なんかじゃないわ

梦石
0
星屑
2208
在线时间
4033 小时
注册时间
2010-10-4
帖子
10779

开拓者贵宾

5
 楼主| 发表于 2014-8-5 17:52:04 | 只看该作者
本帖最后由 taroxd 于 2014-8-5 18:01 编辑
无脑之人 发表于 2014-8-5 17:48
我还以为是用元编程帮GC擦那啥呢……结果是把所有没释放的全部释放掉了吗……
你让Cache储存Bitmap的机制怎 ...


1. 顶楼的脚本只会追踪未释放的对象而不会去释放。
  这是我推荐的方式,也是我自己的使用方式。在实际应用中,擦屁股擦得非常完美。储存在常量中的 bitmap,以及 Cache 中的 bitmap 都不会被追踪到。
2. 沙发的脚本并没有释放 Bitmap,完全不存在这个问题(我也很不喜欢这个脚本,但是有人要)

我不知道你有没有好好看过上面的脚本
回复 支持 反对

使用道具 举报

Lv1.梦旅人

梦石
0
星屑
117
在线时间
552 小时
注册时间
2012-8-18
帖子
1429
4
发表于 2014-8-5 17:48:55 | 只看该作者
我还以为是用元编程帮GC擦那啥呢……结果是把所有没释放的全部释放掉了吗……
你让Cache储存Bitmap的机制怎么办,如果有一直显示的东西【比如鼠标】怎么办……
帮忙找出来的想法挺好,不过擦的不太完美的样子/w\
我要填坑!我要背单词!我要学日语!我要每天锻炼!
好吧呵呵= =
回复 支持 反对

使用道具 举报

Lv5.捕梦者 (暗夜天使)

只有笨蛋才会看到

梦石
1
星屑
21681
在线时间
9419 小时
注册时间
2012-6-19
帖子
7119

开拓者短篇九导演组冠军

3
发表于 2014-8-2 21:23:11 | 只看该作者
实际上我还是觉得制作游戏,至少用RM制作游戏是不需要掌握到这种程度的。在写代码时如果能养成良好的代码习惯即使不使用元编程来检查错误也可以啊。
当然我也不是说不应该掌握那么多,如果只是单纯作为一个程序员或者写脚本的,多掌握总比不会要好。

就我自身的情况来说的话,我是只掌握稍微高于自己必要之上一点点的知识,因为我一直都是单干的,美工音乐剧本等等各个方面都必须分出精力来学习,只是专精脚本的话估计做不出游戏吧【躺

点评

喜闻乐见的打脸  发表于 2014-9-22 12:29
=_=单干很累吧  发表于 2014-8-5 18:04
说的豪放一点:咱才不为别人的脚本擦屁股呢哼  发表于 2014-8-2 21:36
所以就有区别嘛,我是自己单干的,剧本美工什么的也必须出力,没办法在脚本中投入太大的精力,脚本知识自然只稍微到必要之上一点点的地步  发表于 2014-8-2 21:34
制作组用的这些系统,我帮助他们查错而已  发表于 2014-8-2 21:33
回复 支持 反对

使用道具 举报

Lv3.寻梦者 (版主)

…あたしは天使なんかじゃないわ

梦石
0
星屑
2208
在线时间
4033 小时
注册时间
2010-10-4
帖子
10779

开拓者贵宾

2
 楼主| 发表于 2014-8-2 20:51:31 | 只看该作者
本帖最后由 taroxd 于 2014-8-2 20:57 编辑

@3106345123  真的,只要求释放的话毫无难度,但是我是真心希望从根源上解决问题,而不是依靠这一种手段。

RUBY 代码复制
  1. class Scene_Base
  2.   alias auto_dispose_terminate terminate
  3.   def terminate
  4.     auto_dispose_terminate
  5.     [Sprite, Window, Plane, Tilemap, Viewport].each do |klass|
  6.       ObjectSpace.each_object(klass, &:dispose)
  7.     end
  8.   end
  9. end

点评

我也倾向于能从根源上解决问题,不过很多时候,许多人不得不依靠这些手段  发表于 2014-8-2 21:05
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 注册会员

本版积分规则

拿上你的纸笔,建造一个属于你的梦想世界,加入吧。
 注册会员
找回密码

站长信箱:[email protected]|手机版|小黑屋|无图版|Project1游戏制作

GMT+8, 2024-11-25 13:08

Powered by Discuz! X3.1

© 2001-2013 Comsenz Inc.

快速回复 返回顶部 返回列表