昨天发了版本,今天稍微有点空,记录点东西。
大约两三个月前,我追查我们游戏在弱网情况下遇到的一个启动卡住的问题,与同事一起打日志,追到一个类的构造方法时,发现构造方法里的日志打印不出来。我们最初怀疑是不是代码哪里出了问题导致内存爆掉从而使得这个对象的内存分配失败,但通过工具发现并没有出现突然间内存爆涨的情况。没办法,只好重载这个类的new方法并在其中加上日志,看是在哪一步导致这个类的构造函数没有被执行。
当时这个类大约长这个样子:
class A
{
public:
...
public:
somfunction
......
PROP(ClassB, objB);
......
......
somefunciton
private:
bool m_var1;
int m_var2;
};
这个类的头文件大约100来行,初看了一下最底下的数据声明,纯粹的两个字段,重载其new方法后,我们打印其size发现竟然有80多字节。惊不惊喜?于是细看代码,发现了夹在中间的一个属性定义:PROP(ClassB, objB)。一堆方法之中夹了一个属性定义。那么方向就很明确了。肯定是在这个属性定义中,给这个字段 objB 分配内存时失败了,因为成员的内存分配是先于对象的构造函数执行的。
于是我们追进去细看其中代码,发现在这个ClassB的构造方法中,调用了一个方法:getHostByName!这个方法是个线程阻塞的,如果在网络好的情况下,这个方法返回是很快的,但是在弱网下(或者说局域网可用而互联网不可用的时候)这个方法是会一直阻塞在那里的,导致 objB 的构造函数一直阻塞着进一步导致 class A 的构造函数没办法执行。
这里再次记录一下大学里老师讲过的,但不会结合具体场景给学生讲的一个知识点:
构造方法执行顺序:父类->成员变量->自己
析构方法执行顺序:自己->成员变量->父类
这个不起眼的知识点,在关键时刻会帮上大忙。