我正在用C实现带有链接列表的优先级队列,但是当我打印出pop
操作时却遇到了内存泄漏。我还有另一个内存泄漏,我也试图找到它。
请注意,我使用heapusage
代替d99kris
使用Valgrind
。
这是我使用printf
时的堆摘要:
HEAP SUMMARY:
in use at exit: 4112 bytes in 2 blocks
total heap usage: 10 allocs, 17 frees, 4536 bytes allocated
peak heap usage: 4256 bytes allocated
16 bytes in 1 block(s) are lost, originally allocated at:
LEAK SUMMARY:
definitely lost: 4112 bytes in 2 blocks
这是没有printf
的堆摘要:
HEAP SUMMARY:
in use at exit: 16 bytes in 1 blocks
total heap usage: 9 allocs, 10 frees, 440 bytes allocated
peak heap usage: 256 bytes allocated
LEAK SUMMARY:
definitely lost: 16 bytes in 1 blocks
我的pop
函数:
void *prio_q_pop(struct prio_q *q) {
q->size--;
struct elem *temp = q->first;
(q->first) = (q->first)->next;
void *asd = temp->datei;
free(temp);
return asd;
}
还有我的main
函数,我在其中调用printf
struct prio_q *queue;
char *s;
int i;
queue = prio_q_create();
push(queue, "Bye World", 0);
for (i = 0; i < 5; i++) {
s = prio_q_pop(queue);
//printf("%s\n", s);
}
s = prio_q_front(queue);
//printf("%s\n", s);
可能不是 my 代码引起的,可能是内存检查器。以下程序泄漏了1个块,堆使用了2个alloc和4个free。
#include <stdio.h>
int main() {
printf("omer");
return 0;
}
答案 0 :(得分:8)
这是误报。如果有的话,问题是堆没有足够好的文档。我建议使用更好的泄漏检查器,例如泄漏消毒剂或Valgrind。
我创建了文件ERROR: HHH000346: Error during managed flush [Found shared references to a collection: AddressSet.value.addressSet]
org.hibernate.HibernateException: Found shared references to a collection: AddressSet.value.addressSet
at org.hibernate.engine.internal.Collections.processReachableCollection(Collections.java:182)
at org.hibernate.event.internal.FlushVisitor.processCollection(FlushVisitor.java:42)
at org.hibernate.event.internal.AbstractVisitor.processValue(AbstractVisitor.java:104)
at org.hibernate.event.internal.AbstractVisitor.processValue(AbstractVisitor.java:65)
at org.hibernate.event.internal.AbstractVisitor.processValues(AbstractVisitor.java:44)
at org.hibernate.event.internal.AbstractVisitor.processComponent(AbstractVisitor.java:85)
at org.hibernate.event.internal.AbstractVisitor.processValue(AbstractVisitor.java:110)
at org.hibernate.event.internal.AbstractVisitor.processValue(AbstractVisitor.java:65)
at org.hibernate.event.internal.AbstractVisitor.processEntityPropertyValues(AbstractVisitor.java:59)
at org.hibernate.event.internal.DefaultFlushEntityEventListener.onFlushEntity(DefaultFlushEntityEventListener.java:155)
at org.hibernate.event.internal.AbstractFlushingEventListener.flushEntities(AbstractFlushingEventListener.java:216)
at org.hibernate.event.internal.AbstractFlushingEventListener.flushEverythingToExecutions(AbstractFlushingEventListener.java:85)
at org.hibernate.event.internal.DefaultFlushEventListener.onFlush(DefaultFlushEventListener.java:38)
at org.hibernate.internal.SessionImpl.flush(SessionImpl.java:1295)
at org.hibernate.internal.SessionImpl.managedFlush(SessionImpl.java:468)
at org.hibernate.internal.SessionImpl.flushBeforeTransactionCompletion(SessionImpl.java:3135)
at org.hibernate.internal.SessionImpl.beforeTransactionCompletion(SessionImpl.java:2352)
at org.hibernate.engine.jdbc.internal.JdbcCoordinatorImpl.beforeTransactionCompletion(JdbcCoordinatorImpl.java:491)
at org.hibernate.resource.transaction.backend.jdbc.internal.JdbcResourceLocalTransactionCoordinatorImpl.beforeCompletionCallback(JdbcResourceLocalTransactionCoordinatorImpl.java:147)
at org.hibernate.resource.transaction.backend.jdbc.internal.JdbcResourceLocalTransactionCoordinatorImpl.access$100(JdbcResourceLocalTransactionCoordinatorImpl.java:38)
at org.hibernate.resource.transaction.backend.jdbc.internal.JdbcResourceLocalTransactionCoordinatorImpl$TransactionDriverControlImpl.commit(JdbcResourceLocalTransactionCoordinatorImpl.java:231)
at org.hibernate.engine.transaction.internal.TransactionImpl.commit(TransactionImpl.java:65)
at com.ervacon.bitemporal.HibernateTest.testPersistence(HibernateTest.java:77)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:86)
at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:459)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:678)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:382)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:192)
。
test.c
使用泄漏消毒剂,没有错误。
$ cc -fsanitize=leak -g test.c $ ./a.out Hello, world!
使用地址清理器,没有错误。
$ cc -fsanitize=address -g test.c $ ./a.out Hello, world!
使用Valgrind,没有错误。
$ cc -g test.c $ valgrind ./a.out ==189174== Memcheck, a memory error detector ==189174== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al. ==189174== Using Valgrind-3.13.0 and LibVEX; rerun with -h for copyright info ==189174== Command: ./a.out ==189174== Hello, world! ==189174== ==189174== HEAP SUMMARY: ==189174== in use at exit: 0 bytes in 0 blocks ==189174== total heap usage: 1 allocs, 1 frees, 1,024 bytes allocated ==189174== ==189174== All heap blocks were freed -- no leaks are possible ==189174== ==189174== For counts of detected and suppressed errors, rerun with: -v ==189174== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
堆满了,漏了!
$ cc -g test.c $ ./heapusage ./a.out Hello, world! ==189005== Heapusage - https://github.com/d99kris/heapusage ==189005== ==189005== HEAP SUMMARY: ==189005== in use at exit: 1024 bytes in 1 blocks ==189005== total heap usage: 1 allocs, 0 frees, 1024 bytes allocated ==189005== peak heap usage: 1024 bytes allocated ==189005== ==189005== 1024 bytes in 1 block(s) are lost, originally allocated at: ==189005== at 0x00007f99f0de56a7: malloc + 49 ==189005== at 0x00007f99f0a96a32: _IO_file_doallocate + 114 ==189005== at 0x00007f99f0aa4a46: _IO_doallocbuf + 70 ==189005== at 0x00007f99f0aa3da8: _IO_file_overflow + 472 ==189005== at 0x00007f99f0aa2e86: _IO_file_xsputn + 182 ==189005== at 0x00007f99f0a99033: _IO_puts + 211 ==189005== at 0x000055f667ee7655: ==189005== at 0x00007f99f0a502b1: __libc_start_main + 241 ==189005== at 0x000055f667ee755a: ==189005== ==189005== LEAK SUMMARY: ==189005== definitely lost: 1024 bytes in 1 blocks ==189005==
通过挂接malloc和free(并且不扫描内存中的指针)来进行堆工作。在文档中,堆没有充分说明此方法的优缺点。一个优点是它速度快,但缺点是它不精确。
特别是,我会把堆放当作错误信息来称呼:“绝对丢失”一词在这里不适用!
如果您想要更好的错误消息,请使用上面推荐的工具之一:泄漏消毒剂或Valgrind(内存检查)。
总的来说,我还想提醒人们使用这些工具,误报是生活中的事实。程序是否“ Valgrind clean”与程序是否正确是一个不同的问题。
答案 1 :(得分:3)
与Valgrind不同,heapusage
不会跟踪C库为其自身目的分配的内存。 printf
间接导致这种情况,因为流stdout
被行缓冲到终端并被完全缓冲到文件。仅当实际产生输出时,才通过printf
或任何其他输出函数分配流缓冲区。
您可以尝试通过在stdout
函数开始时使main
无缓冲来解决此限制。尝试以下示例:
#include <stdio.h>
int main() {
setvbuf(stdout, NULL, _IONBF, 0);
printf("omer\n");
return 0;
}
如果上面的代码仍然显示有泄漏,请尝试以下替代方法:
#include <stdio.h>
int main() {
char buf[BUFSIZ];
setvbuf(stdout, buf, _IONBF, BUFSIZ);
printf("omer\n");
fclose(stdout);
return 0;
}
还请注意,从stdin
读取输入也将为输入流分配一个缓冲区。程序打开的任何其他流在退出main()
函数之前都应已关闭。关闭流将在后台释放为其分配的所有内存。