java内存加载
⑴ 在系统启动时,使用java怎么将数据加载到内存数组中
我去,这个问题就有点模糊!
既然是系统启动,那么基本是javaWeb的范畴了; 可以有以下几种方法
1: 使用监听器,ServletContextListener这个监听器就是监听系统启动的,然后你实现这个接口,重写init()方法,在里面做一些操作,也就是放进容器内一些数组数据;
2: 把数据写进 .properties文件中,通过spring框架扫描进内存,然后用的时候读出来
⑵ jvm 的内存中可以把一个类加载多次么
1.类加载器深入剖析Java虚拟机与程序的生命周期:当我们执行一个java程序的时候,会启动一个JVM进程,当程序执行完之后,JVM进程就消亡了;在如下情况下JVM将结束声明周期:System.exit(int)方法,当执行这个方法的时候,虚拟机会退出;这个方法传入一个整形参数,这个参数是状态吗:如果这个整形是0的话,就是正常退出,如果不是0的话,就是异常退出;程序正常结束;程序执行过程中,遇到了异常或错误,而异常终止:如果我们在程序中出现了异常,而不去处理,会将异常一直抛给main函数,main函数会将异常抛给JVM,JVM如果处理不了异常,JVM就会异常退出;由于操作系统出现错误导致JVM进程终止:JVM所依赖的平台出现错误,导致JVM终止;2.类的加载,连接和初始化加载:查找并加载类的二进制数据,将class字节码文件加载到内存中;连接:-验证:确保被加载的类的正确性,使用javac编译工具生成的字节码文件能通过验证,如果不是由javac编译生成的字节码文件,如果自己生成的字节码文件不符合JVM虚拟机对字节码文件的要求的话,可能会出现验证通不过的情况;比如说随便拿一个文件,将后缀名直接修改为.class,这样的字节码文件肯定不合法;-准备:为类的静态变量分配内存,并将其初始化为默认值;-解析:把类中的符号引用转为直接引用;初始化:为类的静态变量赋予正确的初始值(正确的值指的是用户赋的值);-好像这个与连接阶段的准备有些重复,在连接的准备阶段只是赋予初始变量,如果用户给这个变量赋了初始值,那么这个变量在连接的准备阶段仍然会赋予初始值;-在这个阶段,才会真正的将初始值赋给静态变量;Java程序对类的使用方式有主动使用和被动使用;所有的JVM实现,必须在每个类或者接口,被java程序“首次主动使用”时才初始化他们;主动使用:创建类的实例;访问某个类或接口的静态变量,或者对该静态变量赋值;调用类的静态方法;反射:Class.forName(“类名”);初始化一个类的子类,看做对父类的主动使用;java虚拟机启动的时候,被标明启动类的类,即包含main方法的类,程序的入口;除了上面6种主动使用之外,其它的情况均为被动使用,其它情况都不会执行第三步初始化;3.类的加载(1)概念类的加载:指的是将类的.class文件中的二进制数据读入到内存中,将其放在运行时数据区的方法区内,然后再堆区创建一个java.lang.Class对象,用来封装类在方法区内的数据结构;反射:反射就是跟句堆区的字节码文件,获取方法去的数据结构;解析:Class对象是由JVM自己创建的,所有的对象都是经过Class对象创建,这个Class对象是反射的入口,通过Class对象,可以关联到目标class字节码文件的内部结构;所有的类对应的Class对象都是唯一的一个,这个类是由JVM进行创建的,并且只有JVM才会创建Class对象;类加载的最终产品是位于堆区中的Class对象,Class对象封装了类在方法区内的数据结构,并且向Java程序员提供了访问方法区内的数据结构的接口(反射用的接口);(2)加载.class文件的方式从本地系统中直接加载:编译好的.class字节码文件直接从硬盘中加载;通过网络下载.class文件:将class字节码文件放在网络空间中,使用URLClassLoader来加载在网络上的.class字节码文件,使用默认的父亲委托机制加载字节码文件;从zip,jar等压缩文件中加载字节码文件:在开发的时候,导入jar包,就是这种方式;从专有的数据库中提取字节码文件;将java源文件动态编译为字节码文件;(3)类加载器lJava虚拟机自带的类加载器:-根类加载器(Bootstrap):是C++写的,程序员无法再java代码中获取这个类,如果使用getClassLoader()方法获取到的是一个null值;packagejvm;Java代码publicclassClassLoaderTest{publicstaticvoidmain(String[]args)throwsException{//java.lang包下的类使用的是跟类加载器进行加载的Classclazz=Class.forName("java.lang.String");System.out.println(clazz.getClassLoader());//自定义的类使用的是应用类加载器(系统加载器)Classclazz2=Class.forName("jvm.C");System.out.println(clazz2.getClassLoader());}}classC{}执行结果:nullJava代码sun.misc.Launcher$AppClassLoader@1372a1a-扩展类加载器(Extension):Java编写;-系统类加载器(应用加载器)(System):Java编写;用户自定义的类加载器:-自定义的类加载器都是java.lang.ClassLoader子类;-用户可以定制类的加载方式String类是由根类加载器进行加载的,我们可以调用Class对象的关于代理中创建对象的类加载器:创建代理对象的时候,动态创建一个类,然后使用指定的类加载器将这个类加载到内存中,然后用加载到内存中的类生成代理对象;创建代理对象的方法:newProxyInstance(ClassLoaderloader,Class[]Interfaces,InvocationHandlerh)loader是定义的代理类的类加载器,中间的接口数组是代理类的要实现的接口列表,h是指派方法调用的调用处理程序;类加载器并不需要在某个类被“首次主动使用”时再加载它:-预加载机制:JVM规范允许类加载器在预料某个类将要被使用的时就预先加载它;-报错时机:如果在预加载的过程中遇到了字节码文件缺失或者存在错误的情况,类加载器会在程序首次主动使用(上面提到的六种情况)该类的时候报错(LinkageError错误);-不报错时机:如果这个错误的字节码文件所对应的类一直没有被使用,那么类加载器就不会报告错误,即便有错误也不会报错;LinkageError:这个错误是Error的子类,程序不能处理这些错误,这些错误都是由虚拟机来处理,这个错误表示出错的是子类,在一定程序上依赖于另一个类,在编译了前面一个类的时候,与后面所依赖的类出现了不兼容的情况;例如:我们使用了jdk1.6在编译一个程序,但是运行环境是jre1.5的,就会出现LinkageError错误;4.类的连接(1)定义类被加载之后,就进入链接阶段;链接:将已读入内存的二进制数据合并到虚拟机的运行时环境中去;链接顾名思义就是讲类与类之间进行关联,例如我们在类A中调用了类B,在链接过程中,就将A与B进行链接,将面向对象语言转化为面向过程语言;(2)类的验证类文件的结构检查:确保类文件遵从java类文件的固定格式,开始类的描述,声明,方法调用格式等;语义检查:确保类本身符合java语言的语法规定,比如final类型的类没有子类,final类型的方法没有被覆盖,在eclipse中这种错误编译的时候不能通过,但是通过其他的方法可以生成错误的字节码文件,这里就是检测恶意生成的字节码文件;字节码验证:确保字节码流可以被JVM安全的执行,字节码流代表java方法(包括静态方法和实例方法),它是由被称作操作码的单字节指令组成的序列,每一个操作码后面跟着一个或多个操作数,字节码验证步骤会检查每个操作码是否合法,即是否有着合法的操作数;下面是指令码组成的序列,类似于微指令:Jvm编译指令代码代码//CompiledfromByteToCharCp1122.java(version1.5:49.0,superbit)publicclasssun.io.ByteToCharCp1122extendssun.io.ByteToCharSingleByte{//Fielddescriptor#17Lsun/nio/cs/ext/IBM1122;privatestaticfinalsun.nio.cs.ext.IBM1122nioCoder;//Methoddescriptor#18()Ljava/lang/String;//Stack:1,Locals:1publicjava.lang.StringgetCharacterEncoding();0ldc[1]2areturnLinenumbers:[pc:0,line:25]//Methoddescriptor#2()V//Stack:2,Locals:1publicByteToCharCp1122();0aload_0[this]1invokespecialsun.io.ByteToCharSingleByte()[25]4aload_0[this]5getstaticsun.io.ByteToCharCp1122.nioCoder:sun.nio.cs.ext.IBM1122[23]8invokevirtualsun.nio.cs.ext.IBM1122.getDecoderSingleByteMappings():java.lang.String[27]11putfieldsun.io.ByteToCharSingleByte.byteToCharTable:java.lang.String[24]14returnLinenumbers:[pc:0,line:28][pc:4,line:29][pc:14,line:30]//Methoddescriptor#2()V//Stack:2,Locals:0static{};0newsun.nio.cs.ext.IBM1122[15]3p4invokespecialsun.nio.cs.ext.IBM1122()[26]7putstaticsun.io.ByteToCharCp1122.nioCoder:sun.nio.cs.ext.IBM1122[23]10returnLinenumbers:[pc:0,line:22]}l二进制兼容性的验证:确保相互引用的类之间协调一致的;例如在A类的a()方法中调用B类的b()方法,JVM在验证A类的时候,会验证B类的b()方法,加入b()方法不存在,或者版本不兼容(A,B两类使用不同的JDK版本编译),会抛出NoSuchMethodError错误;(3)准备阶段在准备阶段,JVM为类的静态变量分配内存空间,并设置默认的初始值.例如下面的Sample类,在准备阶段,为int类型的静态变量分配4个字节,并赋予初始值0;为long类型的静态变量b,分配8个字节,并赋予初始值0;PS:在java中基本类型变量占用的空间是一定的,java运行在JVM上的,在C中,就要根据平台变化而变化了;publicclassSample{Java代码privatestaticinta=1;privatestaticlongb;static{b=2;}(4)类的解析在解析阶段,JVM会把类的二进制数据中的符号引用替换为直接引用,例如在A类中的a()方法引用B类中的b()方法;在A类的二进制数据中包含了一个对B类的b()方法的符号引用,这个符号引用由b()方法的全名和相关的描述符组成,在Java解析阶段,就会把这个符号引用替换为指针,这个指针就是C语言中的指针了,该指针指向B类的b()方法在方法区中的内存位置,这个指针就是直接引用;5.类的初始化在初始化阶段,Java虚拟机执行类的初始化操作,为类的静态变量赋予初始值,在程序中,静态变量初始化有两种途径:直接在声明处进行初始化,例如下面的Sample中的变量a;在静态代码块中进行初始化,例如下面的Sample中的变量b;Java代码publicclassSample{privatestaticinta=1;privatestaticlongb;static{b=2;}}6.面试题介绍Java代码publicclassPrepareOrInit{publicstaticvoidmain(String[]args){Singletonsingleton=Singleton.getInstance();System.out.println(singleton.count1);System.out.println(singleton.count2);}}classSingleton{=newSingleton();publicstaticintcount1;publicstaticintcount2=0;privateSingleton(){count1++;count2++;}(){returnsingleton;}}执行结果:10分析:这段代码与类的链接中的准备阶段和初始化阶段有关系,准备阶段是给静态的字段赋予默认值,初始化阶段给静态变量赋予正确的值,即用户的值;在主函数中,调用了类的静态方法,相当于主动使用,这里调用了类的静态方法;之后进行连接的准备操作,给类中的静态变量赋予初值,singleton值为null,count1与count2值为0;执行初始化操作,给类中的静态变量赋予正确的值,给singleton变量赋予正确的值,调用构造方法,此时count1与count2执行自增操作,两者都变成1,然后执行count1的赋予正确值操作,这里用户没有赋值操作,count2用户进行了赋值为0的操作,0将原来的1覆盖掉了,因此结果为1,0;Java代码publicclassPrepareOrInit{publicstaticvoidmain(String[]args){Singletonsingleton=Singleton.getInstance();System.out.println(singleton.count1);System.out.println(singleton.count2);}}classSingleton{publicstaticintcount1;publicstaticintcount2=0;=newSingleton();privateSingleton(){count1++;count2++;}(){returnsingleton;}}执行结果:11在准备阶段count1和count2都赋值为0,然后在初始化阶段,全部赋值为1;
⑶ java中的静态成员是否是在类加载的时候就被加载到内存中了
对,首先可以肯定的是你的说法是正确的。
类加载的过程有以下几个过程:加载、验证、准备、解析
在准备的过程中,静态成员变量就会进行内存分配,而不包括实例变量,实例变量是在对象实例化随着对象一起分配在java堆中的。但是要注意一点,比如:public static int a=1;那变量在准备阶段过后的初始值是0而不是1.因为这个时候尚未执行任何的Java方法。而是赋值了一个初始值,如果是引用成员变量就会为null,其他基本类型对应的是其基本默认值。
ps:以上资料来自《深入Jvm虚拟机》
⑷ java io流是将数据全部加载在内存的流对象里才开始读的吗
是的,但你说的不全。读取有俩种方式,一种是直接读取,一种是先放在缓冲流中,再一并读取。内存只是个中转站,在第一种方式中,数据是先加载在内存区,然后又被马上被读取出去。在第二种方式中,它会有个缓冲区,其实也就是个可以重用的内存区,它是先读取完放在缓冲区,然后一并读取出去!
⑸ class 类 什么 时候被 加载 java虚拟机内存
编写的java文件经过编译之后形成字节码文件,当你的程序在运行中调用到该class类的时候,通过ClassLoader进行加载。下面详细介绍下。
class文件从加载到jvm内存中开始,到卸载出内存为止,他的整个生命周期(整个加载过程)包括:加载,验证,准备,解析,初始化,使用和卸载。其中验证,准备,解析三个合称为连接。下面重点说一下加载过程。
加载过程:
1、通过一个类的全限定名来获取定义此类的二进制字节流
2、将字节流所代表的静态存储结构转化为方法区的运行时存储结构
3、在java堆中生成一个代表该类的对象,作为方法区这些数据的访问入口
验证:
1、文件格式验证:是否以魔数0xCAFEBABE开头,class文件的主次版本号是否在当前jvm处理范围之内,常量池的常量中是否有不被支持的类型,指向常量中的索引值有无不存在的常量,等
2、元数据验证:对字节码描述的信息进行语义分析,保证符合java规范。如是否有父类,是否继承了不允许继承的类,如果不是抽象类,是否实现了所有未实现的方法。等
3、字节码验证:数据流和控制流分析。主要针对类的方法体。
4、符号引用验证:如符号引用中通过字符串描述的全限定名是否能够找到对应的类等
准备:
该阶段正式为类变量分配内存并设置初始值。内存在方法区中分配。这里说的初始值是通常情况下说的零值。
解析:
虚拟机将常量池中的符号引用替换为直接引用的过程。包括:
1、类或接口的解析
2、字段解析
3、类方法解析
4、接口方法解析
初始化:
类初始化阶段是类加载过程的最后一步,除了加载阶段用户可以通过自定义加载器参与外,其余动作完全由虚拟机指导控制。到了初始化阶段,才真正开始执行类中定义的java程序代码(字节码)。在准备阶段,变量已经赋过一次系统默认值,而在初始化阶段,则是根据程序制定的主观计划去初始化类变量和其他资源,即初始化阶段是执行类构造器<clinit>()方法的过程。<clinit>是在编译java源码时,按照静态块和静态变量赋值语句的顺序生成的。如果类没有静态块也没有为静态变量赋值,就不会生成<clinit>方法,该方法只能被虚拟机调用。
⑹ java中内存有方法区,类加载时方法和静态属性都会随类加载到其中 为什么类名不能直接调用非静态
类加载,网上有相关介绍在第一次创建一个类的对象或者第一次调用一个类的静态属性和方法的时候,会发生类加载类加载期间,如果发现有静态属性,就给对应的静态属性分配内存空间,并赋值这个过程完成之后,今后在调用该类的静态属性,虚拟机会直接寻找改属性先前已经分配的内存空间地址,然后调用其值。同样,修改这个类的静态属性也一样说白了,静态属性将永远占用某一块固定的内存空间,知道程序终止但是这里有点说不通,假如一个静态的字符串,运行过程中,不断修改这个字符串的值,那么其内存空间就不可能固定,所以可以认为这个静态字符串的引用是固定的
⑺ JAVA:类的字节码文件被加载到内存是什么意思
程序运行前都要进入内存,Java程序也一样,运行前装入内存
⑻ 小白求教,java中的.class文件是在程序执行到new的时候靠加载器加载到内存的
main方法是静态的,随类的加载而加载,类首先开辟了内存空间,这时main也就已经存在了,在运行时java虚拟机首先找的是main方法
⑼ java普通方法和静态方法的加载(注意是加载)时机相同吗存储方法耗费内存空间吗
静态方法放在Date数据区 可以直接调用 和STATIC 变量一样的 在内存开始加载的时候就有空间了 普通方法是 程序运行到的时候才开始调用直接用
⑽ java中的全局变量和静态变量是在编译时分配内存还是在加载时分配内存
全局变量是在创建对象的时候分配内存的 创建对象过程为
分配空间。
递归的创建父类对象。
初始化成员变量。
调用构造方法创建一个对象。
静态变量是在类加载的时候分配空间的,静态变量和对象没有关系 是在JVM第一次读到一个类的时候加载信息的过程中分配空间的 加载过程为
1 .加载父类(如果父类已经加载过,则不在加载)。
2.初始化静态属性 。
3 .按顺序的初始化静态代码块 初始化的前提就是分配空间 。
而且静态变量在以后的创建对象的时候不在初始化 所以一般用静态来保存共享信息