It's going to be a more sinister article, and you might want to Google it when you're trying to create a tragedy in someone's life. In the Java world, memory overflows are just a bug you might introduce in this situation. Your victims will spend a few days in the office, or even weeks of sleepless nights.
In this article I'll introduce two ways to overflow, which are easier to understand and reproduce. And they are all case studies of real-world projects, but I've simplified them for you to get a clear grip.
But rest assured that a similar case will be more prevalent than you might think, after we've encountered and solved an overflow bug.
First comes the entry state, when using Hashset/hashmap, the key value is not or the Equals ()/hashcode () method is incorrect, which can lead to a notorious error.
Class Keylessentry {
static class Key {
Integer id;
Key (Integer id) {
this.id = ID;
}
@Override public
int hashcode () {return
id.hashcode ();
}
}
public static void Main (string[] args) {
Map m = new HashMap ();
while (true)
for (int i = 0; I < 10000 i++)
if (!m.containskey (i))
m.put (new Key (i), "number:" + i);
}
}
When you run the above code, you may expect it to run forever, after all, the built-in caching scheme will only increase to 10,000 elements, and then no more, all keys are already in HashMap. However, it is not so. The element will continue to grow because the key class does not implement an appropriate equals () method after Hashcode ().
The workaround is simple, as long as you add a equals method just like the following example. But before you find the problem, you must have spent a lot of valuable brain cells.
@Override public
Boolean equals (Object o) {
Boolean response = false;
if (o instanceof Key) {
response = ((Key) O. id). Equals (this.id);
return response;
}
The next one you have to remind friends of is the operation related to string processing. It's going to be weird, especially when it comes to differences in JVM versions. The internal working mechanism of string is changed in the JDK 7u6, so if you find that the product environment is only a minor difference, then you are ready for the condition. Debug your friends with code like this, and ask him why the bug only appears in the product.
Class Stringer {
static final int MB = 1024*512;
static String createlongstring (int length) {
StringBuilder sb = new StringBuilder (length);
for (int i=0 i < length; i++)
sb.append (' a ');
Sb.append (System.nanotime ());
return sb.tostring ();
}
public static void Main (string[] args) {
List substrings = new ArrayList ();
for (int i=0; i< i++) {
String longstr = createlongstring (MB);
String subStr = longstr.substring (1,10);
Substrings.add (SUBSTR);}}
What's wrong with the code above? When it runs on a version prior to JDK 7u6, the returned string will hold a reference to the 1M or so size string, and if you run it to-xmx100m, you will get an unexpected oom error. With the difference between the platform and the version in your experimental environment, the brain damage is done.
Now if you want to cover up your footprint, we can introduce some more advanced concepts. Like what
- Loading destructive code in a different class loader, keeping a reference to the loaded class after it is deleted by the original ClassLoader, can simulate a class loader overflow
- Hiding aggressive code in a Finalize method makes the program performance unpredictable
- By adding a tricky combination to a long-running thread, it may hold something in the threadlocals that can be accessed by the thread pool to manage the application thread.
I hope we have given you some raw material for thinking and some material when you want to fix someone. This will result in endless debugging. Unless your friend uses PLUMBR to find the location of the overflow.