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

Lines changed: 8 additions & 0 deletions
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

Lines changed: 163 additions & 0 deletions
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

Lines changed: 178 additions & 0 deletions
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)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy