Java
Java中各种内存溢出案例 - 图1

定义主类结构

首先,创建一个名称为 BlowUpJVM 的类,之后所有的案例实验都是基于这个类进行,如下所示。

  1. public class BlowUpJVM {
  2. }

栈深度溢出

  1. public static void testStackOverFlow(){
  2. BlowUpJVM.testStackOverFlow();
  3. }

栈不断递归,而且没有处理,所以虚拟机栈就不断深入不断深入,栈深度就这样溢出了。

永久代内存溢出

  1. public static void testPergemOutOfMemory1(){
  2. //方法一失败
  3. List<String> list = new ArrayList<String>();
  4. while(true){
  5. list.add(UUID.randomUUID().toString().intern());
  6. }
  7. }

打算把 String 常量池堆满,没想到失败了,JDK1.7 后常量池放到了堆里,也能进行垃圾回收了。
然后换种方式,使用 cglib,用 Class 把老年代堆满。

  1. public static void testPergemOutOfMemory2(){
  2. try {
  3. while (true) {
  4. Enhancer enhancer = new Enhancer();
  5. enhancer.setSuperclass(OOM.class);
  6. enhancer.setUseCache(false);
  7. enhancer.setCallback(new MethodInterceptor() {
  8. @Override
  9. public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
  10. return proxy.invokeSuper(obj, args);
  11. }
  12. });
  13. enhancer.create();
  14. }
  15. }
  16. catch (Exception e){
  17. e.printStackTrace();
  18. }
  19. }

虚拟机成功内存溢出了,那 JDK 动态代理产生的类能不能溢出呢?

  1. public static void testPergemOutOfMemory3(){
  2. while(true){
  3. final OOM oom = new OOM();
  4. Proxy.newProxyInstance(oom.getClass().getClassLoader(), oom.getClass().getInterfaces(), new InvocationHandler() {
  5. public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
  6. Object result = method.invoke(oom, args);
  7. return result;
  8. }
  9. });
  10. }
  11. }

事实表明,JDK 动态代理产生的类不会造成内存溢出,原因是:JDK 动态代理产生的类信息,不会放到永久代中,而是放在堆中。

本地方法栈溢出

  1. public static void testNativeMethodOutOfMemory(){
  2. int j = 0;
  3. while(true){
  4. Printer.println(j++);
  5. ExecutorService executors = Executors.newFixedThreadPool(50);
  6. int i=0;
  7. while(i++<10){
  8. executors.submit(new Runnable() {
  9. public void run() {
  10. }
  11. });
  12. }
  13. }
  14. }

这个的原理就是不断创建线程池,而每个线程池都创建 10 个线程,这些线程池都是在本地方法区的,久而久之,本地方法区就溢出了。

JVM栈内存溢出

  1. public static void testStackOutOfMemory(){
  2. while (true) {
  3. Thread thread = new Thread(new Runnable() {
  4. public void run() {
  5. while(true){
  6. }
  7. }
  8. });
  9. thread.start();
  10. }
  11. }

线程的创建会直接在 JVM 栈中创建,但是本例子中,没看到内存溢出,主机先挂了,不是 JVM 挂了,真的是主机挂了,无论在 mac 还是在 windows,都挂了。 :::danger 温馨提示,这个真的会死机的。 :::

堆溢出

  1. public static void testOutOfHeapMemory(){
  2. List<StringBuffer> list = new ArrayList<StringBuffer>();
  3. while(true){
  4. StringBuffer B = new StringBuffer();
  5. for(int i = 0 ; i < 10000 ; i++){
  6. B.append(i);
  7. }
  8. list.add(B);
  9. }
  10. }

不断往堆中塞新增的 StringBuffer 对象,堆满了就直接溢出了。

测试案例完整代码

  1. public class BlowUpJVM {
  2. //栈深度溢出
  3. public static void testStackOverFlow(){
  4. BlowUpJVM.testStackOverFlow();
  5. }
  6. //不能引起永久代溢出
  7. public static void testPergemOutOfMemory1(){
  8. //方法一失败
  9. List<String> list = new ArrayList<String>();
  10. while(true){
  11. list.add(UUID.randomUUID().toString().intern());
  12. }
  13. }
  14. //永久代溢出
  15. public static void testPergemOutOfMemory2(){
  16. try {
  17. while (true) {
  18. Enhancer enhancer = new Enhancer();
  19. enhancer.setSuperclass(OOM.class);
  20. enhancer.setUseCache(false);
  21. enhancer.setCallback(new MethodInterceptor() {
  22. @Override
  23. public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
  24. return proxy.invokeSuper(obj, args);
  25. }
  26. });
  27. enhancer.create();
  28. }
  29. }
  30. catch (Exception e){
  31. e.printStackTrace();
  32. }
  33. }
  34. //不会引起永久代溢出
  35. public static void testPergemOutOfMemory3(){
  36. while(true){
  37. final OOM oom = new OOM();
  38. Proxy.newProxyInstance(oom.getClass().getClassLoader(), oom.getClass().getInterfaces(), new InvocationHandler() {
  39. public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
  40. Object result = method.invoke(oom, args);
  41. return result;
  42. }
  43. });
  44. }
  45. }
  46. //本地方法栈溢出
  47. public static void testNativeMethodOutOfMemory(){
  48. int j = 0;
  49. while(true){
  50. Printer.println(j++);
  51. ExecutorService executors = Executors.newFixedThreadPool(50);
  52. int i=0;
  53. while(i++<10){
  54. executors.submit(new Runnable() {
  55. public void run() {
  56. }
  57. });
  58. }
  59. }
  60. }
  61. //JVM内存溢出
  62. public static void testStackOutOfMemory(){
  63. while (true) {
  64. Thread thread = new Thread(new Runnable() {
  65. public void run() {
  66. while(true){
  67. }
  68. }
  69. });
  70. thread.start();
  71. }
  72. }
  73. //堆溢出
  74. public static void testOutOfHeapMemory(){
  75. List<StringBuffer> list = new ArrayList<StringBuffer>();
  76. while(true){
  77. StringBuffer B = new StringBuffer();
  78. for(int i = 0 ; i < 10000 ; i++){
  79. B.append(i);
  80. }
  81. list.add(B);
  82. }
  83. }
  84. }