Memory leak reason

本文详细阐述了常见的Java内存问题,包括高内存使用、HTTPsession不当使用导致的问题、错误的缓存使用、高事务内存消耗、过大临时对象的影响、ClassLoader问题以及错误的hashcode和equals方法实现引发的问题。

1. 高内存使用

现在的生产环境都是10,20GB以上的内存,而代码方面的不严谨不修改,盲目的增加内存,加大heap,会导致当系统在大内存下进行gc时的STW的时间变长,这是最常见的高内存使用问题

2. HTTP session 当做cache

因为http session的易用性,很多开发把太多的object存放在HTTP session中,导致session越来越大(每个用户一个),而且session的销毁一般都是30分钟以上(为了保证用户使用性),导致内存中的session非常庞大而且释放不了(高负载,高并发情况下)

同时,OSIV的技术手段会把hibernate session也放在HTTP session中(更大了)

memory leak不知不觉就发生了

更别说,还有session replication等技术手段,更慢更容易出错

3. 错误的cache使用

在cache中存放过多的东西,导致memory leak, 这里说的更多的是tomcat 本地缓存

过多的缓存,导致GC

有的缓存的实现,使用了soft reference,将会在GC的时候清除,缓存过多导致GC频繁(只有在GC之后剩余空间不足的情况下,才会GC 软引用),这种缓存引起的错误很难被发现,会被错误的看成eden空间不足等

4. 高事物内存

如果每个事物的内存使用很高,会导致Young代很快达到Minor GC标准,如果是高并发情况下大量事物内存,导致Young满了,然后Minor GC频发,然而那些事物内存对象又没有die,所以几次之后就被移到了Old中,问题很快潜伏下来,知道最后爆发,而且光靠OutofMemory Error发生那一刻的heap,可能看不出这个问题的存在,只有full heap dump才可能

5. 太大的临时对象

比如XML文件解析等,一次性读取了太大的对象

6. ClassLoader问题

Class load在Perm中,一般都是比较稳定的大小,一旦Perm满了,class再要load没有空间就报OutOfMemory Error错误了

发生这类问题的原因可能有:

6.1 太多的类,类有太多的方法,类变量等,这种问题一般在应用启动的时候就抛错了

6.2 同一个类在jvm中存在多个,发生这种情况的原因主要是因为jvm中因为不同的classcloader加载同一个类,使一个类存在多份,同样的jar包,可能因为此原因被多次加载

6.3 同一个class被重复的加载,在某些情况下,一个class只被使用一点时间,那么方法区的classes信息也是会被GC的,不同的JVM,HotSpot只会在FULL GC的时候做,而IBM和JRockit则每次GC都会,那么这个class就会成为问题,不同的被load,GC,load,GC....

7错误的hashcode和equals方法

当实现错误的hashcode和equals方法后,对象可能会被不断添加到map中,导致内存爆表,而且这种问题很难通过heap dump发现,最好通过代码检查工具来做

 

原文:

http://apmblog.compuware.com/2011/12/15/the-top-java-memory-problems-part-2/

 

 

#include <libubus.h> #include <stdio.h> #include <stdlib.h> #include <string.h> // 定义参数解析结构 enum { MEMORY_LEAK_AMOUNT, // TRIGGER_NULL_POINTER, __PARAM_MAX }; static const struct blobmsg_policy mem_policy[__PARAM_MAX] = { [MEMORY_LEAK_AMOUNT] = {.name = "leak_amount", .type = BLOBMSG_TYPE_INT32}, // [TRIGGER_NULL_POINTER] = {.name = "trigger_null", .type = BLOBMSG_TYPE_BOOL}, }; // 全局变量用于持续内存泄漏 static char **global_leaked_memory = NULL; static int leak_count = 0; static int ubus_trigger_memory_leak(struct ubus_context *ctx, struct ubus_object *obj, struct ubus_request_data *req, const char *method, struct blob_attr *msg) { struct blob_attr *tb[__PARAM_MAX]; int leak_amount = 0; int leak_count = 0; char **global_leaked_memory = NULL; // 假设已声明为全局变量 // 解析参数 blobmsg_parse(mem_policy, __PARAM_MAX, tb, blob_data(msg), blob_len(msg)); if (tb[MEMORY_LEAK_AMOUNT]) { leak_amount = blobmsg_get_u32(tb[MEMORY_LEAK_AMOUNT]); } // 创建响应缓冲区 struct blob_buf b = {}; blob_buf_init(&b, 0); // 触发多种内存泄漏类型 if (leak_amount > 0) { // 类型1:常规malloc泄漏 void *leak1 = malloc(leak_amount * 1024); if (leak1) { memset(leak1, 0xAA, leak_amount * 1024); global_leaked_memory = realloc(global_leaked_memory, (leak_count + 1) * sizeof(char *)); global_leaked_memory[leak_count++] = leak1; } // 类型2:calloc泄漏 void *leak2 = calloc(leak_amount / 2, 2048); if (leak2) { memset(leak2, 0xBB, leak_amount * 1024); global_leaked_memory = realloc(global_leaked_memory, (leak_count + 1) * sizeof(char *)); global_leaked_memory[leak_count++] = leak2; } // 类型3:realloc泄漏 void *leak3 = realloc(NULL, leak_amount * 2048); if (leak3) { memset(leak3, 0xCC, leak_amount * 2048); global_leaked_memory = realloc(global_leaked_memory, (leak_count + 1) * sizeof(char *)); global_leaked_memory[leak_count++] = leak3; } blobmsg_add_u32(&b, "total_leaked_kb", leak_amount * 4); // 估算总泄漏量 blobmsg_add_u32(&b, "leaked_objects", leak_count); } blobmsg_add_string(&b, "status", leak_amount > 0 ? "memory_leaked" : "no_leak_requested"); ubus_send_reply(ctx, req, b.head); blob_buf_free(&b); return UBUS_STATUS_OK; } static int ubus_trigger_null_pointer(struct ubus_context *ctx, struct ubus_object *obj, struct ubus_request_data *req, const char *method, struct blob_attr *msg) { struct blob_attr *tb[__PARAM_MAX]; int severity = 1; bool trigger_crash = true; // 实际启用时设置为true // 解析参数 blobmsg_parse(mem_policy, __PARAM_MAX, tb, blob_data(msg), blob_len(msg)); if (tb[MEMORY_LEAK_AMOUNT]) { // 复用参数作为严重级别 severity = blobmsg_get_u32(tb[MEMORY_LEAK_AMOUNT]); severity = (severity % 4) + 1; // 限制在1-4范围内 } // 创建响应缓冲区 struct blob_buf b = {}; blob_buf_init(&b, 0); blobmsg_add_u32(&b, "severity", severity); const char *action_desc = ""; // 根据严重级别触发不同类型的空指针访问 switch (severity) { case 1: // 简单空指针解引用 action_desc = "simple_null_dereference"; if (trigger_crash) { int *null_ptr = NULL; *null_ptr = 42; // 实际触发崩溃 } break; case 2: // 空函数指针调用 action_desc = "null_function_call"; if (trigger_crash) { void (*func)(void) = NULL; func(); // 实际触发崩溃 } break; case 3: // 空结构体成员访问 action_desc = "null_struct_member_access"; if (trigger_crash) { struct Test { int value; } *test = NULL; int val = test->value; // 实际触发崩溃 } break; case 4: // 空指针数组访问 action_desc = "null_array_access"; if (trigger_crash) { int *array = NULL; array[0] = 99; // 实际触发崩溃 } break; } blobmsg_add_string(&b, "action", action_desc); blobmsg_add_string(&b, "status", trigger_crash ? "crash_triggered" : "dry_run"); if (!trigger_crash) { blobmsg_add_string(&b, "warning", "Run_with_trigger_crash=true_to_actually_crash"); } ubus_send_reply(ctx, req, b.head); blob_buf_free(&b); // 如果实际触发崩溃,此处不会执行 return trigger_crash ? UBUS_STATUS_INTERNAL_ERROR : UBUS_STATUS_OK; } static struct ubus_method memleak_ubus_methods[] = { UBUS_METHOD("leak_memory", ubus_trigger_memory_leak, mem_policy), UBUS_METHOD("null_pointer", ubus_trigger_null_pointer, mem_policy), }; static struct ubus_object_type memleak_ubus_obj_type = UBUS_OBJECT_TYPE("memory_leak", memleak_ubus_methods); static struct ubus_object memleak_ubus_obj = { .name = "memory_leak", .type = &memleak_ubus_obj_type, .methods = memleak_ubus_methods, .n_methods = ARRAY_SIZE(memleak_ubus_methods), }; // 清理函数 void cleanup_leaked_memory(void) { for (int i = 0; i < leak_count; i++) { free(global_leaked_memory[i]); } free(global_leaked_memory); global_leaked_memory = NULL; leak_count = 0; } int mem_ubus_init(struct ubus_context *ctx) { if (ctx == NULL) { printf("ubus context is null.\n"); return -1; } if (ubus_add_object(ctx, &cstg_ubus_obj) != 0) { printf("ubus add obj failed\n"); return -1; } return 0; } 修改上述代码,创建主子双进制,子进程触发内存泄漏,父进程回收子进程或者终止子进程并返回消息
09-04
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值