Skip to content

Commit ff5a6e7

Browse files
committed
HashMap内存溢出
1 parent ed2e582 commit ff5a6e7

File tree

3 files changed

+349
-0
lines changed

3 files changed

+349
-0
lines changed

README.md

+8
Original file line numberDiff line numberDiff line change
@@ -252,6 +252,14 @@ String s = " z111,c888,n222,,,g000, t333,a999,c888 ,p000 ,z111 ";
252252
253253
```
254254

255+
* 上海票牛网(感觉问的问题是最难的公司)
256+
257+
*1.[Spring bean依赖注入的时候怎么解决bean之间的循环引用问题](https://www.cnblogs.com/bhlsheji/p/5208076.html)
258+
259+
*2.如何自己去实现一个WEB框架,怎么实现类似于SpringMVC里的@RequestMapping
260+
261+
* 拍拍贷
262+
* [使用HashMap的时候如何避免内存泄漏](readme/)
255263

256264
## 更多Java面试题
257265

code/HashMapMemoryLeak.java

+163
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,163 @@
1+
import java.util.HashMap;
2+
import java.util.Map;
3+
4+
/**
5+
* 演示HashMap的内存泄漏产生过程, 参考test()方法
6+
*/
7+
public class HashMapMemoryLeak {
8+
public static void main(String[] args) {
9+
// test1();
10+
test2();
11+
}
12+
13+
private static void test1() {
14+
Map<Person, Integer> map = new HashMap<Person, Integer>();
15+
Person p = new Person("zhangsan", 12);
16+
17+
System.out.println("step1 p=" + p);
18+
System.out.println("System.identityHashCode(p)=" + System.identityHashCode(p));
19+
map.put(p, 1);
20+
21+
p.setName("lisi"); // 因为p.name参与了hash值的计算,修改了之后hash值发生了变化,所以下面删除不掉
22+
23+
System.out.println("System.identityHashCode(p)=" + System.identityHashCode(p));
24+
25+
map.remove(p);
26+
27+
System.out.println("step2 p=" + p);
28+
System.out.println("hashCOde" + p.hashCode());
29+
30+
System.out.println("---------After remove()-------");
31+
System.out.println("map.size() " + map.size());
32+
System.out.println("map.containsKey(p) " + map.containsKey(p));
33+
System.out.println("map.get(p) " + map.get(p));
34+
}
35+
36+
private static void test2() {
37+
Map<Person, Integer> map = new HashMap<Person, Integer>();
38+
Person p = new Person("zhangsan", 12);
39+
40+
map.put(p, 1);
41+
42+
System.out.println("map.get(p)="+ map.get(p));
43+
Person p2 = new Person("zhangsan", 13);
44+
45+
System.out.println("p.hashCode() == p2.hashCode() ? " + (p.hashCode() == p2.hashCode()));
46+
47+
System.out.println("p.equals(p2) " + p.equals(p2));
48+
System.out.println("map.size() " + map.size());
49+
System.out.println("map.get(p)="+ map.get(p));
50+
System.out.println("map.get(p2)="+ map.get(p2));
51+
52+
// 在setName之前,假设p对应的KV对存放的位置是X
53+
54+
p.setName("lisi"); // 这里造成p2这个key的数据内存泄漏。导致后面无法被删除掉
55+
// p.setName("lisi")后, 这个Node存放的Entry的未知不变,但是,里面Node里面的Key的name变里,导致key的hashCode()变了。
56+
// 后面通过map.get(p)去检索数据的时候,p.hashCode()变了,经过hash计算后得到位置是Y(Y此时为空)
57+
// 所以这时候map.get(p)返回null, map.containsKey(p)返回false
58+
// 所以位置X处的Node数据已经无法进行get() ,put(),containsKey(), remove()的操作了
59+
// 这个内存因为被HashMap引用,也无法GC,
60+
// 这快内存不能被删也不能鄂博查询,也不能被GC回收,称之为内存泄漏(memory leak)
61+
// 这才几个字节的内存泄漏,还不会出事故, 大量的内存泄漏,会浪费很多内存,降低系统性能。最终浪费大量的堆内存,
62+
// 最终导致内存溢出(Out of memory, OutOfMemoryException, 或者报错Heap Space...)
63+
// 结论: 大量的内存泄漏会最终导致内存溢出。 如果出现了内存溢出的报错,我们可以去查看代码,是否有内存泄漏,
64+
// 或者到map里查看是否有一些无意义的垃圾数据(极有可能是内存溢出的数据)
65+
66+
map.remove(p);
67+
// map.remove(p2);
68+
69+
System.out.println("---------After map.remove(p)-------------");
70+
System.out.println("map.size() " + map.size());
71+
System.out.println("map.get(p)="+ map.get(p));
72+
System.out.println("map.get(p2)="+ map.get(p2));
73+
74+
}
75+
}
76+
77+
class Person {
78+
private String name;
79+
private int age;
80+
81+
public Person(String name, int age) {
82+
super();
83+
this.name = name;
84+
this.age = age;
85+
}
86+
87+
public String getName() {
88+
return name;
89+
}
90+
91+
public void setName(String name) {
92+
this.name = name;
93+
}
94+
95+
public int getAge() {
96+
return age;
97+
}
98+
99+
public void setAge(int age) {
100+
this.age = age;
101+
}
102+
103+
// @Override
104+
// public boolean equals(Object obj) {
105+
// return super.equals(obj);
106+
// }
107+
108+
@Override
109+
public boolean equals(Object obj) {
110+
if (obj == null) {
111+
return false;
112+
}
113+
114+
if (obj instanceof Person) {
115+
Person personValue = (Person)obj;
116+
117+
if (personValue.getName() == null && name ==null) {
118+
return true;
119+
}
120+
121+
if (personValue.getName() != null && personValue.getName().equals(name)){
122+
return true;
123+
}
124+
}
125+
126+
return false;
127+
}
128+
129+
// @Override
130+
// public boolean equals(Object obj) {
131+
// if (obj == null) {
132+
// return false;
133+
// }
134+
//
135+
// if (obj instanceof Person) {
136+
// Person personValue = (Person)obj;
137+
//
138+
// if (personValue.getName() == null && name ==null && personValue.getAge() ==age) {
139+
// return true;
140+
// }
141+
//
142+
// if (personValue.getName() != null && personValue.getName().equals(name) && personValue.getAge() ==age){
143+
// return true;
144+
// }
145+
// }
146+
//
147+
// return false;
148+
// }
149+
150+
// @Override
151+
// public boolean equals(Object obj) {
152+
// if (obj == null) {
153+
// return false;
154+
// }
155+
//
156+
// return hashCode() == obj.hashCode();
157+
// }
158+
159+
@Override
160+
public int hashCode() {
161+
return name.hashCode() * 123;
162+
}
163+
}

readme/hashmap-memory-leak.md

+178
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,178 @@
1+
# 使用HashMap的时候如何避免内存泄漏
2+
3+
当Key为复杂类型(自定义对象)的时候,如果map.put(obj, value)后
4+
如果修改了obj中的某些字段值,而且这这个字段会导致obj.hashCode()发送变化的。就必定导致内存泄漏。
5+
6+
结果是后面如论是进行map.get(obj), remove(obj), containsKey(obj)都没有意义。
7+
map.get(obj)==null, remove(obj)操作无效, containsKey(obj)==false
8+
这条数据无法被GC处理。 称之为内存泄漏。 大量的内存泄漏,最终导致内存溢出异常。
9+
10+
代码见[HashMapMemoryLeak.java](/code/HashMapMemoryLeak.java)
11+
12+
贴出来如下
13+
```java
14+
import java.util.HashMap;
15+
import java.util.Map;
16+
17+
/**
18+
* 演示HashMap的内存泄漏产生过程, 参考test()方法
19+
*/
20+
public class HashMapMemoryLeak {
21+
public static void main(String[] args) {
22+
// test1();
23+
test2();
24+
}
25+
26+
private static void test1() {
27+
Map<Person, Integer> map = new HashMap<Person, Integer>();
28+
Person p = new Person("zhangsan", 12);
29+
30+
System.out.println("step1 p=" + p);
31+
System.out.println("System.identityHashCode(p)=" + System.identityHashCode(p));
32+
map.put(p, 1);
33+
34+
p.setName("lisi"); // 因为p.name参与了hash值的计算,修改了之后hash值发生了变化,所以下面删除不掉
35+
36+
System.out.println("System.identityHashCode(p)=" + System.identityHashCode(p));
37+
38+
map.remove(p);
39+
40+
System.out.println("step2 p=" + p);
41+
System.out.println("hashCOde" + p.hashCode());
42+
43+
System.out.println("---------After remove()-------");
44+
System.out.println("map.size() " + map.size());
45+
System.out.println("map.containsKey(p) " + map.containsKey(p));
46+
System.out.println("map.get(p) " + map.get(p));
47+
}
48+
49+
private static void test2() {
50+
Map<Person, Integer> map = new HashMap<Person, Integer>();
51+
Person p = new Person("zhangsan", 12);
52+
53+
map.put(p, 1);
54+
55+
System.out.println("map.get(p)="+ map.get(p));
56+
Person p2 = new Person("zhangsan", 13);
57+
58+
System.out.println("p.hashCode() == p2.hashCode() ? " + (p.hashCode() == p2.hashCode()));
59+
60+
System.out.println("p.equals(p2) " + p.equals(p2));
61+
System.out.println("map.size() " + map.size());
62+
System.out.println("map.get(p)="+ map.get(p));
63+
System.out.println("map.get(p2)="+ map.get(p2));
64+
65+
// 在setName之前,假设p对应的KV对存放的位置是X
66+
67+
p.setName("lisi"); // 这里造成p2这个key的数据内存泄漏。导致后面无法被删除掉
68+
// p.setName("lisi")后, 这个Node存放的Entry的未知不变,但是,里面Node里面的Key的name变里,导致key的hashCode()变了。
69+
// 后面通过map.get(p)去检索数据的时候,p.hashCode()变了,经过hash计算后得到位置是Y(Y此时为空)
70+
// 所以这时候map.get(p)返回null, map.containsKey(p)返回false
71+
// 所以位置X处的Node数据已经无法进行get() ,put(),containsKey(), remove()的操作了
72+
// 这个内存因为被HashMap引用,也无法GC,
73+
// 这快内存不能被删也不能鄂博查询,也不能被GC回收,称之为内存泄漏(memory leak)
74+
// 这才几个字节的内存泄漏,还不会出事故, 大量的内存泄漏,会浪费很多内存,降低系统性能。最终浪费大量的堆内存,
75+
// 最终导致内存溢出(Out of memory, OutOfMemoryException, 或者报错Heap Space...)
76+
// 结论: 大量的内存泄漏会最终导致内存溢出。 如果出现了内存溢出的报错,我们可以去查看代码,是否有内存泄漏,
77+
// 或者到map里查看是否有一些无意义的垃圾数据(极有可能是内存溢出的数据)
78+
79+
map.remove(p);
80+
// map.remove(p2);
81+
82+
System.out.println("---------After map.remove(p)-------------");
83+
System.out.println("map.size() " + map.size());
84+
System.out.println("map.get(p)="+ map.get(p));
85+
System.out.println("map.get(p2)="+ map.get(p2));
86+
87+
}
88+
}
89+
90+
class Person {
91+
private String name;
92+
private int age;
93+
94+
public Person(String name, int age) {
95+
super();
96+
this.name = name;
97+
this.age = age;
98+
}
99+
100+
public String getName() {
101+
return name;
102+
}
103+
104+
public void setName(String name) {
105+
this.name = name;
106+
}
107+
108+
public int getAge() {
109+
return age;
110+
}
111+
112+
public void setAge(int age) {
113+
this.age = age;
114+
}
115+
116+
// @Override
117+
// public boolean equals(Object obj) {
118+
// return super.equals(obj);
119+
// }
120+
121+
@Override
122+
public boolean equals(Object obj) {
123+
if (obj == null) {
124+
return false;
125+
}
126+
127+
if (obj instanceof Person) {
128+
Person personValue = (Person)obj;
129+
130+
if (personValue.getName() == null && name ==null) {
131+
return true;
132+
}
133+
134+
if (personValue.getName() != null && personValue.getName().equals(name)){
135+
return true;
136+
}
137+
}
138+
139+
return false;
140+
}
141+
142+
// @Override
143+
// public boolean equals(Object obj) {
144+
// if (obj == null) {
145+
// return false;
146+
// }
147+
//
148+
// if (obj instanceof Person) {
149+
// Person personValue = (Person)obj;
150+
//
151+
// if (personValue.getName() == null && name ==null && personValue.getAge() ==age) {
152+
// return true;
153+
// }
154+
//
155+
// if (personValue.getName() != null && personValue.getName().equals(name) && personValue.getAge() ==age){
156+
// return true;
157+
// }
158+
// }
159+
//
160+
// return false;
161+
// }
162+
163+
// @Override
164+
// public boolean equals(Object obj) {
165+
// if (obj == null) {
166+
// return false;
167+
// }
168+
//
169+
// return hashCode() == obj.hashCode();
170+
// }
171+
172+
@Override
173+
public int hashCode() {
174+
return name.hashCode() * 123;
175+
}
176+
}
177+
178+
```

0 commit comments

Comments
 (0)