为什么语言选择在JVM上实现-1

深入理解JVM内

JVM——多语言的平台

一个完整的语言有 前端、优化、后端、runtime、库 ,JVM 把后面四个都给包办了。
编译器是分前端和后端的,参见前端和后端
编译器中,前端程序设计语言源代码转换成一种中间形式,然后后端再将它转换成计算机能够运行的二进制代码。后端还经常对代码进行优化以提升程序的运行效率。前端后端的区别能够将处理原代码的语法解析器和生成机器码和对代码进行优化的后端去分开。

  1. 非常经济地实现跨平台。你的语言编译器后端只需要输出 JVM 字节码就可以。跨平台需要极大的工作量,举个例子,只是独立开发生成本地代码,就需要花费大量精力去针对不同平台和处理器进行优化(比如 Firefox 就会考虑重用 WebKit 的这部分后端)。
  2. JVM 卓越的 JIT (Just-In-Time 即时编译)性能。 JIT 可以在运行中记录程序运行的特征,并在其基础上做大量的优化(Java 企业级应用的优秀性能很大程度上是由此而来)。 JIT 自从 HotSpot JVM 随 Java 1.2 发布以来,JVM JIT 的性能不断提高,是无可争议的成功产品。把 JVM 作为目标平台意味着大量的性能优化工作可以「外包」给 JVM 来做,大大缩减了 Guest 语言的开发预算。
  3. 已经有多个成熟的实例,有大量的经验可以借鉴
  4. JVM 作为一个成熟的高层运行环境,为 Guest 语言提供了很多运行时所需要的服务,比如内存管理(有业界领先的垃圾回收等),很大程度上避免了额外的独立开发。
  5. JVM 有多个独立实现,也有若干厂商会持续推进,资料完备,社区巨大。
  6. Java 社区有大量成熟的库,一般来说,运行在 JVM 上的其它语言都会设计一个专用的「桥」来帮助直接使用 Java 的库,对潜在客户来说是个很好的卖点。
  7. Java 有还算不错的开发工具和环境。目标为 JVM 的很多语言会考虑用 Java 语言实现(至少在 bootstrap 阶段)。

 

内存泄露:指程序中动态分配内存给一些临时对象,但是对象不会被GC所回收,它始终占用内存。即被分配的对象可达但已无用

 

内存溢出:指程序运行过程中无法申请到足够的内存而导致的一种错误。内存溢出通常发生于OLD段或Perm段垃圾回收后,仍然无内存空间容纳新的Java对象的情况。

 

从定义上可以看出内存泄露是内存溢出的一种诱因,不是唯一因素。

内存泄露的几种场景:

 

1、长生命周期的对象持有短生命周期对象的引用

 

            这是内存泄露最常见的场景,也是代码设计中经常出现的问题。

            例如:在全局静态map中缓存局部变量,且没有清空操作,随着时间的推移,这个map会越来越大,造成内存泄露。

 

2、修改hashset中对象的参数值,且参数是计算哈希值的字段

 

             当一个对象被存储进HashSet集合中以后,就不能修改这个对象中的那些参与计算哈希值的字段,否则对象修改后的哈希值与最初存储进HashSet集合中时的哈希值就不同了,在这种情况下,即使在contains方法使用该对象的当前引用作为参数去HashSet集合中检索对象,也将返回找不到对象的结果,这也会导致无法从HashSet集合中删除当前对象,造成内存泄露。

 

3、机器的连接数和关闭时间设置

 

            长时间开启非常耗费资源的连接,也会造成内存泄露。

内存溢出场景:

 jvm管理的内存大致包括三种不同类型的内存区域:Permanent Generation space(永久保存区域)、Heap space(堆区域)、Java Stacks(Java栈)。其中永久保存区域主要存放Class(类)和Meta的信息,Class第一次被Load的时候被放入PermGen space区域,Class需要存储的内容主要包括方法和静态属性、常量。堆区域用来存放Class的实例(即对象)、数组,对象需要存储的内容主要是非静态属性。每次用new创建一个对象实例后,对象实例存储在堆区域中,这部分空间也被jvm的垃圾回收机制管理。而Java栈跟大多数编程语言包括汇编语言的栈功能相似,主要基本类型变量以及方法的输入输出参数。Java程序的每个线程中都有一个独立的堆栈。容易发生内存溢出问题的内存空间包括:Permanent Generation space和Heap space。

第一种 方法区内存溢出(OutOfMemoryError: PermGen space )
发生这种问题的原意是程序中使用了大量的jar或class,使java虚拟机装载类的空间不够,与Permanent Generation space有关。解决这类问题办法:
1. 增加java虚拟机中的XX:PermSize和XX:MaxPermSize参数的大小,其中XX:PermSize是初始永久保存区域大小,XX:MaxPermSize是最大永久保存区域大小。如针对tomcat6.0,在catalina.sh 或catalina.bat文件中一系列环境变量名说明结束处(大约在70行左右) 增加一行:
JAVA_OPTS=" -XX:PermSize=64M -XX:MaxPermSize=128m"
如果是windows服务器还可以在系统环境变量中设置。感觉用tomcat发布sprint+struts+hibernate架构的程序时很容易发生这种内存溢出错误。使用上述方法,我成功解决了部署ssh项目的tomcat服务器经常宕机的问题。

内存溢出时的错误信息为:

             outOfMemoryError:permgem space

package com.jony.java;  
  
import java.lang.reflect.Method;  
import net.sf.cglib.proxy.Enhancer;  
import net.sf.cglib.proxy.MethodInterceptor;  
import net.sf.cglib.proxy.MethodProxy;  
  
public class TestMethodAreaOverflow {  
  
    static class MethodAreaOom {}  
  
    public static void main(String[] args) {  
        while (true) {  
            Enhancer enhancer = new Enhancer();  
            enhancer.setSuperclass(MethodAreaOom.class);  
            enhancer.setUseCache(false);  
            enhancer.setCallback(new MethodInterceptor() {  
                  
                @Override  
                public Object intercept(Object arg0, Method arg1, Object[] arg2,  
                        MethodProxy proxy) throws Throwable {  
                    return proxy.invoke(arg0, arg2);  
                }  
            });  
            enhancer.create();  
        }  
    }  
  
}  

 
第二种 堆内存溢出(OutOfMemoryError:  Java heap space
发生这种问题的原因是java虚拟机创建的对象太多,在进行垃圾回收之间,虚拟机分配的到堆内存空间已经用满了,与Heap space有关。解决这类问题有两种思路:
1. 检查程序,看是否有死循环或不必要地重复创建大量对象。找到原因后,修改程序和算法。
2. 增加Java虚拟机中Xms(初始堆大小)和Xmx(最大堆大小)参数的大小。如:set JAVA_OPTS= -Xms256m -Xmx1024m

* 堆内存溢出 
* 
* jvm参数:-Xms5m -Xmx5m -Xmn2m -XX:NewSize=1m 
* 
*/  
public class MemoryLeak {  
     
    private String[] s = new String[1000];  
   
    public static void main(String[] args) throws InterruptedException {  
        Map<String,Object> m =new HashMap<String,Object>();  
        int i =0;  
        int j=10000;  
        while(true){  
            for(;i<j;i++){  
                MemoryLeak memoryLeak = new MemoryLeak();  
                m.put(String.valueOf(i), memoryLeak);  
            }  
        }  
    }  
}

 


第三种 线程栈溢出(java.lang.StackOverflowError)

程栈时线程独有的一块内存结构,所以线程栈发生问题必定是某个线程运行时产生的错误。

       一般线程栈溢出是由于递归太深或方法调用层级过多导致的。

       发生栈溢出的错误信息为:

              java.lang.StackOverflowError

栈的基本特点就是FILO(First In Last Out),如果in的太多而out的太少,就可能overflow了。而Java Method Statck的功能就是保存每一次函数调用时的“现场”,即为入栈,函数返回对应出栈,所以函数的调用深度越大,栈就变得越大,足够大的时候就会溢出。所以模拟Java Method Stack溢出,只要不断递归调用某一函数就可以导致溢出。

package com.jony.java;  
  
public class TestStackOverflow {  
    private int stackLength = 0;  
  
    public void stackOverFlow(){  
        ++stackLength;  
        stackOverFlow();  
    }  
  
    public static void main(String[] args){  
        TestStackOverflow test = new TestStackOverflow();  
        try {  
            test.stackOverFlow();  
        } catch (Throwable e) {  
            System.out.println("Stack Length:" + test.stackLength);  
            e.printStackTrace();  
        }  
    }  

 

参数的含义
-vmargs -Xms128M -Xmx512M -XX:PermSize=64M -XX:MaxPermSize=128M
-vmargs 说明后面是VM的参数,所以后面的其实都是JVM的参数了
-Xms128m JVM初始分配的堆内存
-Xmx512m JVM最大允许分配的堆内存,按需分配
-XX:PermSize=64M JVM初始分配的非堆内存
-XX:MaxPermSize=128M JVM最大允许分配的非堆内存,按需分配

 

posted @ 2016-07-04 16:18  He_quotes  阅读(115)  评论(0)    收藏  举报