不正确的异常处理/对象释放可能导致内存泄漏(原因:未被注意到 "variable might not be assigned" 警告)
Improper exception handling / object freeing probably causes memory leak (Cause: unnoticed "variable might not be assigned" warning)
具有相同问题的示例的完整来源
Full source of example with same problem
继续使用单个 EXE 中的所有代码
我已将所有内容移动到单个 EXE
var SenderInstance: AutoGeneratedWebserviceUnit.Sender;
...
procedure TForm1.FormCreate(Sender: TObject);
begin
SenderInstance := AutoGeneratedWebserviceUnit.GetSender(False, 'http://invalid_URL'); // => there is no exception here
end;
...
procedure TForm1.Button1Click(Sender: TObject);
var
req: AutoGeneratedWebserviceUnit.Request;
res: AutoGeneratedWebserviceUnit.Response;
begin
try
req := Request.Create;
try
with req do
begin
ID := 0;
param := 'trash';
end;
res := SenderInstance.Request('Login', 'Pass', req); // => ESOAPHTTPException + EAccesViolation !
ShowMessage(res.status);
finally
req.Free;
res.Free; // ### MOST POSSIBLE PROBLEM CAUSE ###
end;
except
on E: Exception do
ShowMessage(E.Message);
end;
end;
我理解 ESOAPHTTPException,但是 AV?为什么?...
问题
引发 ESOAPHTTPException 后发生奇怪的事情...
我想可能是内存泄漏之类的事情我无法弄清楚,或者我做了一些非常非常愚蠢的事情...
有什么设置吗?也许我忘记了什么?
线索
我发现了一些东西
关于 try/finally 块内的 "res.Free;" 行
禁用此行不会导致 AV
但是已分配(请求)returns 真
已分配(res) returns 也对...
什么……?
导致 AV 的问题在于 Button1Click
事件处理程序中的 try..finally
子句。
您正在尝试释放您的 res
变量(响应),自调用 Request
方法以来,该变量 尚未 分配任何内容失败并出现异常。这意味着它包含垃圾,并且通过调用 res.Free
您正在访问您不应该访问的内存位置,这几乎会引发任何类型的奇怪错误。
要解决这个问题,请在进入try..finally
之前将res设置为nil
,并在调用res.Free
.
之前检查它是否已分配。
附带说明一下,这是因为 res
是一个 local 变量。如果它是 class 实例的 成员,编译器会自动为其分配一个 nil
值。
更新
正如@RobKennedy 所说,使用 nested try..finally
块比分配 nil
要好得多,就像我第一次告诉你的那样(这可能会导致如果某些析构函数失败会出现问题)。
所以你会做如下事情:
req := Request.Create;
try
req.ID := 0;
req.param := 'trash';
res := SenderInstance.Request('Login', 'Pass', req);
try
ShowMessage(res.status);
finally
res.Free;
end;
finally
req.Free;
end;
具有相同问题的示例的完整来源
Full source of example with same problem
继续使用单个 EXE 中的所有代码
我已将所有内容移动到单个 EXE
var SenderInstance: AutoGeneratedWebserviceUnit.Sender;
...
procedure TForm1.FormCreate(Sender: TObject);
begin
SenderInstance := AutoGeneratedWebserviceUnit.GetSender(False, 'http://invalid_URL'); // => there is no exception here
end;
...
procedure TForm1.Button1Click(Sender: TObject);
var
req: AutoGeneratedWebserviceUnit.Request;
res: AutoGeneratedWebserviceUnit.Response;
begin
try
req := Request.Create;
try
with req do
begin
ID := 0;
param := 'trash';
end;
res := SenderInstance.Request('Login', 'Pass', req); // => ESOAPHTTPException + EAccesViolation !
ShowMessage(res.status);
finally
req.Free;
res.Free; // ### MOST POSSIBLE PROBLEM CAUSE ###
end;
except
on E: Exception do
ShowMessage(E.Message);
end;
end;
我理解 ESOAPHTTPException,但是 AV?为什么?...
问题
引发 ESOAPHTTPException 后发生奇怪的事情... 我想可能是内存泄漏之类的事情我无法弄清楚,或者我做了一些非常非常愚蠢的事情...
有什么设置吗?也许我忘记了什么?
线索
我发现了一些东西 关于 try/finally 块内的 "res.Free;" 行 禁用此行不会导致 AV 但是已分配(请求)returns 真 已分配(res) returns 也对...
什么……?
导致 AV 的问题在于 Button1Click
事件处理程序中的 try..finally
子句。
您正在尝试释放您的 res
变量(响应),自调用 Request
方法以来,该变量 尚未 分配任何内容失败并出现异常。这意味着它包含垃圾,并且通过调用 res.Free
您正在访问您不应该访问的内存位置,这几乎会引发任何类型的奇怪错误。
要解决这个问题,请在进入try..finally
之前将res设置为nil
,并在调用res.Free
.
附带说明一下,这是因为 res
是一个 local 变量。如果它是 class 实例的 成员,编译器会自动为其分配一个 nil
值。
更新
正如@RobKennedy 所说,使用 nested try..finally
块比分配 nil
要好得多,就像我第一次告诉你的那样(这可能会导致如果某些析构函数失败会出现问题)。
所以你会做如下事情:
req := Request.Create;
try
req.ID := 0;
req.param := 'trash';
res := SenderInstance.Request('Login', 'Pass', req);
try
ShowMessage(res.status);
finally
res.Free;
end;
finally
req.Free;
end;