写在前面
作为程序员, 多多少少都会遇到一些内存溢出的场景, 如果你还没遇到, 说明你工作的年限可能比较短, 或者你根本就是个假程序员! 哈哈, 开个玩笑. 今天, 我们就以 Java 代码的方式来列举几个典型的内存溢出案例, 希望大家在日常工作中, 尽量避免写这些 low 水平的代码.
定义主类结构
首先, 我们创建一个名称为 BlowUpJVM 的类, 之后所有的案例实验都是基于这个类进行. 如下所示.
- public class BlowUpJVM {
- }
栈深度溢出
- public static void testStackOverFlow(){
- BlowUpJVM.testStackOverFlow();
- }
栈不断递归, 而且没有处理, 所以虚拟机栈就不断深入不断深入, 栈深度就这样溢出了.
永久代内存溢出
- public static void testPergemOutOfMemory1(){
- // 方法一失败
- List<String> list = new ArrayList<String>();
- while(true){
- list.add(UUID.randomUUID().toString().intern());
- }
- }
打算把 String 常量池堆满, 没想到失败了, JDK1.7 后常量池放到了堆里, 也能进行垃圾回收了.
然后换种方式, 使用 cglib, 用 Class 把老年代取堆满
- public static void testPergemOutOfMemory2(){
- try {
- while (true) {
- Enhancer enhancer = new Enhancer();
- enhancer.setSuperclass(OOM.class);
- enhancer.setUseCache(false);
- enhancer.setCallback(new MethodInterceptor() {
- @Override
- public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
- return proxy.invokeSuper(obj, args);
- }
- });
- enhancer.create();
- }
- }
- catch (Exception e){
- e.printStackTrace();
- }
- }
虚拟机成功内存溢出了, 那 JDK 动态代理产生的类能不能溢出呢?
- public static void testPergemOutOfMemory3(){
- while(true){
- final OOM oom = new OOM();
- Proxy.newProxyInstance(oom.getClass().getClassLoader(), oom.getClass().getInterfaces(), new InvocationHandler() {
- public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
- Object result = method.invoke(oom, args);
- return result;
- }
- });
- }
- }
事实表明, JDK 动态代理差生的类不会造成内存溢出, 原因是: JDK 动态代理产生的类信息, 不会放到永久代中, 而是放在堆中.
本地方法栈溢出
- public static void testNativeMethodOutOfMemory(){
- int j = 0;
- while(true){
- Printer.println(j++);
- ExecutorService executors = Executors.newFixedThreadPool(50);
- int i=0;
- while(i++<10){
- executors.submit(new Runnable() {
- public void run() {
- }
- });
- }
- }
- }
这个的原理就是不断创建线程池, 而每个线程池都创建 10 个线程, 这些线程池都是在本地方法区的, 久而久之, 本地方法区就溢出了.
JVM 栈内存溢出
- public static void testStackOutOfMemory(){
- while (true) {
- Thread thread = new Thread(new Runnable() {
- public void run() {
- while(true){
- }
- }
- });
- thread.start();
- }
- }
线程的创建会直接在 JVM 栈中创建, 但是本例子中, 没看到内存溢出, 主机先挂了, 不是 JVM 挂了, 真的是主机挂了, 无论在 Mac 还是在 Windows, 都挂了.
温馨提示, 这个真的会死机的.
堆溢出
- public static void testOutOfHeapMemory(){
- List<StringBuffer> list = new ArrayList<StringBuffer>();
- while(true){
- StringBuffer B = new StringBuffer();
- for(int i = 0 ; i <10000 ; i++){
- B.append(i);
- }
- list.add(B);
- }
- }
不断往堆中塞新增的 StringBuffer 对象, 堆满了就直接溢出了.
测试案例完整代码
- public class BlowUpJVM {
- // 栈深度溢出
- public static void testStackOverFlow(){
- BlowUpJVM.testStackOverFlow();
- }
- // 不能引起永久代溢出
- public static void testPergemOutOfMemory1(){
- // 方法一失败
- List<String> list = new ArrayList<String>();
- while(true){
- list.add(UUID.randomUUID().toString().intern());
- }
- }
- // 永久代溢出
- public static void testPergemOutOfMemory2(){
- try {
- while (true) {
- Enhancer enhancer = new Enhancer();
- enhancer.setSuperclass(OOM.class);
- enhancer.setUseCache(false);
- enhancer.setCallback(new MethodInterceptor() {
- @Override
- public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
- return proxy.invokeSuper(obj, args);
- }
- });
- enhancer.create();
- }
- }
- catch (Exception e){
- e.printStackTrace();
- }
- }
- // 不会引起永久代溢出
- public static void testPergemOutOfMemory3(){
- while(true){
- final OOM oom = new OOM();
- Proxy.newProxyInstance(oom.getClass().getClassLoader(), oom.getClass().getInterfaces(), new InvocationHandler() {
- public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
- Object result = method.invoke(oom, args);
- return result;
- }
- });
- }
- }
- // 本地方法栈溢出
- public static void testNativeMethodOutOfMemory(){
- int j = 0;
- while(true){
- Printer.println(j++);
- ExecutorService executors = Executors.newFixedThreadPool(50);
- int i=0;
- while(i++<10){
- executors.submit(new Runnable() {
- public void run() {
- }
- });
- }
- }
- }
- //JVM 内存溢出
- public static void testStackOutOfMemory(){
- while (true) {
- Thread thread = new Thread(new Runnable() {
- public void run() {
- while(true){
- }
- }
- });
- thread.start();
- }
- }
- // 堆溢出
- public static void testOutOfHeapMemory(){
- List<StringBuffer> list = new ArrayList<StringBuffer>();
- while(true){
- StringBuffer B = new StringBuffer();
- for(int i = 0 ; i < 10000 ; i++){
- B.append(i);
- }
- list.add(B);
- }
- }
- }
写在最后
如果觉得文章对你有点帮助, 请微信搜索并关注「 冰河技术 」微信公众号, 跟冰河学习高并发编程技术.
最后, 附上并发编程需要掌握的核心技能知识图, 祝大家在学习并发编程时, 少走弯路.
来源: https://www.cnblogs.com/binghe001/p/12952772.html