【转载】PhantomReference and Finalization

本文翻译至:PhantomReference and Finalization

软引用,弱引用以及虚引用

软引用(SoftReferences),比较典型的应用是在内存缓存的场景中。JVM会尽可能地将对象保留在内存中,当JVM内存不足的时候,才会从最早的references开始清除。根据javadoc中的描述,整个清除过程是没有保障的。

弱引用(WeakReferences)是我最经常使用的类型。典型的用途是在创建一些弱引用的监听器(Listener),或者是想收集某个对象的额外信息(使用WeakHashMap)的场景中。非常有助于降低类耦合度

其实笔者读到这里的时候,是产生了一些疑问的。为何使用weakHashMap可以降低类耦合度?设想一下使用了WeakHashMap的场景,weakhashmap可以优雅的解决内存释放的问题,但是如果没有WeakHashMap的话,那么实现就会复杂许多。可以在对象不在使用的时候,将它从Map中移除。这就需要容易管理者构造一个清理的函数给对象调用者使用,或者使用一个监听器模式。比如在编写一些使用者非常广泛的api类型的代码时候(比如jdk的api),添加这样的函数可能会使使用者的api变得非常复杂。

虚引用(Phantom references)则适用于在垃圾回收之前进行的预处理,比如需要释放一些资源的场景。遗憾的是,很多开发者会使用finalize()方法去执行这些操作,这并不是一个好的方式。finalize方法如果没有小心的使用在恰当的线程,恰当的时机,那么很可能会对应用造成可怕的性能影响,甚至会影响应用的数据完整性。

在虚引用的构造方法中,开发者需要显式的指定一个ReferenceQueue去将已经标记为“phantom reachable”的对象加入ReferenceQueue队列中。“phantom reachable”代表连虚引用本身都引用不到的对象。最令人迷惑的是即使Phantom references继续保持着私有对象的引用(区别于软引用以及弱引用),get方法也会返回一个null。这样一来,一旦进入这个状态的对象就无法再一次获得强引用。

开发者可以一次一次的对ReferenceQueue调用poll()方法,检测是否有新的PhantomReferences进入“phantom reachable”状态。正常的写法中,可以使类继承于java.lang.ref.PhantomReference,以保证引用的对象只垃圾回收一次,然后无法继续被获取。

PhantomReference 以及 finalization的细节

对PhantomReference 来说,最常见的误解会认为它是被设计用来“修复”finalizers 带来的对象逃逸问题。举个例子来说,我们常常会这么说:

虚引用可以避免finalize()带来的基础问题:finalize()方法可以通过创建一个新的强引用,使自身免于垃圾回收而进行“逃逸”。所以,重写了finalize()方法的对象,需要至少在两条分别的垃圾回收链中,才会被正确的回收。

然而,使用了虚引用,也有可能使对象出现逃逸,请看以下的代码

由此可见,表面上看,引用类型非常有可能是通过成员变量 Reference#referent 指向那些已经失去引用的对象。但是实际上,垃圾回收器对对特定的对象产生了一个意外。这一现象也直接对上文中的结论产生了冲突:

虚引用只用对象在实际的内存空间中被移除时候,才会执行enqueued操作。

到底哪种说法是正确的, javadoc是这样说明的:

Phantom references are most often used for scheduling pre-mortem cleanup

所以,如果虚引用并不是设计用来修正finalize逃逸的问题(这个问题非常严肃,曾经被Lazarus、Jesus 以及许多其他学者指出),那么虚引用究竟有什么作用?

finalize()方法实际是通过垃圾回收线程去执行的,即使在简单的单线程应用中,考虑到潜在因素,也可能出现并发问题(比如错误的将共享状态放入同步方法中等)。但是使用了虚引用的话,你可以制定执行出队操作的线程(在单线程程序中,指定的线程会周期性的做这个任务)

使用 WeakReference 的话,会如何?

弱引用看起来也会满足垃圾回收之前的内存清理场景。区别在于合适进行引用的入队操作。PhantomReference会在执行finalization之后入队,而WeakReference会在之前。对于finalize()方法中没有关键实现的对象来说,不受影响。

但是对于那些需要在finalize()方法中执行一些清理的对象,就会有些许不同

(PhantomReference’s get() 方法总是返回null)。开发者需要存储尽可能多的状态信息,去进行清理操作。举个例子,清理array中的对象,设置为null以后,开发者需要记录下来array中对象的下标,方便后续跟踪查看。对于这类型操作,可以将类继承于PhantomReference,然后创建这个类的实例。

下面更进一步的说说。

想象一个场景:一名开发者准备在某个对象中编写一段清除钩子的代码(通过 finalize()或者是通过[Weak|Phantom]Reference),当这个对象仅仅有属于线程栈空间的强引用(比如局部变量)的时候,开发者调用了一个方法,那么这时,可能发生这样的事情:

出于性能的考虑,JVM会检测是否这个对象有失去引用的可能。所以,在执行方法的过程中,finalization 可能被并发的执行。这样可能导致一些不可预料的结果(finalization 可能修改了一些类内部的状态,比如其他方法也会使用这些状态)。这种情况非常罕见,可以采取以下的方式修复:

这种情况仅仅适用于那些仅仅在线程栈中持有的对象:
– 对象重写了finalize()方法。
– 有一个[Weak|Soft|PhantomReference]引用指向这个对象,同时已经进入了ReferenceQueue,有另外一个线程进行dequeue的操作

总结一下,最安全的清理机制,是通过PhantomReference以及ReferenceQueue,在同一个线程下进行清理。如果是启用了另一个线程,那么就需要使用同步方法快

记一次php+apache调优

近日手头拿到了一个调优问题,详细一看,发现是apache+php问题,问题的具体表现如下:

正常运行的服务常常停止响应, 一旦停止响应, 维护人员就不得己需要去重启apache服务

对php不熟,调查过程也顺便学习一下php的基本配置。

2015-3-11 调试

首先去服务器进程看了看,发现有一个 http.exe 在后台一直占用%50左右的 cpu,即使通过 Moniter 结束了服务,这个进程也依然坚挺,始终占用固定的CPU, 打开perfmon.exe,发现 内存换页频繁,且都是硬错误,估计就是那个进程导致的,直接结束之。 结果cpu一下子就下来了。

又研究了部分参数设置,尤其是 mpm_module 相关的设置
通过以下命令可以查看加载的模块

发现mpm_module的的加载情况和实际配置不符,windows系统中,其实只加载了后者,实际配置如下:

apache官方文档中有如下说明

AcceptEx()是一个微软的WinSock2 API ,通过使用BSD风格的accept() API提供了性能改善。一些流行的Windows产品,比如防病毒软件或虚拟专用网络软件,会干扰AcceptEx()的正确操作。如果遇到类似于如下的错误:

[error] (730038)An operation was attempted on something that is not a socket.: winnt_accept: AcceptEx failed. Attempting to recover.

就需要使用这个指令来禁止使用AcceptEx() 。

对于windows操作系统,可以使用专用的mpmwinntmodule,来提高性能,但是参数配置要注意。

2015-3-18 调试

观察了几天,发现还是有问题,找错误日志去看了看,发现了一个问题

每次运行到一个阶段,apache就会出现无法回收内存的严重问题,这样一来,用不了多久就得重启了

经过调查,从apache的 bug 一路找过去,最后发现这个是php的一个 bug,但是都没有解决。。。

最后又找了一阵子,发现一个 论坛 中有讨论,找到一句靠谱的话

Looks like php related.
You are using outdated/legacy PHP 4 and Apache 2.0, not anymore supported for quite some time.

It is a known issue with a PHP before 5.3 that it has memory management and stability issues.

#The most common solution for this kinfd of errors was using PHP with Fast CGI (mod_fcgid). I do not know there is still somewhere a version for Apache 2.0.

Also I see also PHP warnings: Undefined variable: idvue libellevue.

随后我找了一个php5.2.7稳定版进行升级,升级以后,做了简单的并发访问,没有打印异常。
继续观察观察

Java知识探究一:关于IO类库

经过组织考察,令我忽然发觉自己在最常用的Java中也有很多不明白的地方,实为平身一大憾事,今天特意抽时间将这些点滴记录下来,与大家一起分享

第一批想整理的知识点如下:

  1. Java的IO探究,IO的整个结构与发展,顺带附上公司某小工写的断点续传代码学习。
  2. Java的异常机制,关于编译时异常和运行时异常的探究。
  3. JavaCommon包的理解,尤其是collection包的一些小看法,其实容器嘛,什么样的Utils也逃不出一些基本的范畴,比如存、取、排序、安全性、校验等等等。

闲话不多说,先开始今天的主题,研究一下IO的整个结构

从体系结构上划分,IO系统总共分为两大模块, IO和NIO(非阻塞),IO诞生于JDK1.4之前,JDK1.4时,产生了NIO,并且借用NIO重构了部分IO的代码,比如FileInputStream中增加了对NIO进行支持的getChannel()方法,再比如Reader和FileReader基本用nio全部重写了。

一、Think in IO

IO从实现上,大致分为字节流和字符流两种:

  1. 字节流。对文件的读写操纵以字节为单位,说的直白一点,就是操作byte,byte数组。对应无符号整数的话,就是read方法的正常返回值范围在[0,255]之间,范围有限的返回值有很多优点,比较有代表性的一个就是可以流来做一个简单的zip实现,算法的话,采用huffman树。当然,一个一个字节操作的话,效率不高,利用Buffer则效率提高不少。但是字节流有个问题,那就是在操作文本文件的时候,对于编码会有很多多余的代码,例子如下
    FileInputStream is = new FileInputStream("F:\\books\\base\\vim常用指令.txt");
            byte[] buff = new byte[BUFFER_SIZE];
            int readSize = 0;
            while ((readSize = is.read(buff)) != -1)
            {
                System.out.println(readSize);
                if(readSize<1024){
                    byte[] tmp = new byte[readSize];
                    System.arraycopy(buff, 0, tmp, 0, readSize);
                    System.out.print(new String(tmp, "GBK"));
                }else{
                    System.out.print(new String(buff, "GBK"));
                }
            }
    
  2. 字符流。以字符作为单元进行操作,Reader内部实现其实就是以char或者char数组作为缓存容器的。操作文本文件时候方便许多。编码采用系统默认的编码格式。找了好久才找到代码的说+_+,代码隐藏的很深,从Reader找到ImputStreamReader,再到StreamDecoder再到nio包中的Charset,最终是优先获取系统中的环境变量,System.getProperties()也可以获取,windows7中文版的话,获取到的是“ file.encoding=GB18030”
    /**
         * Returns the default charset of this Java virtual machine.
         *
         * <p> The default charset is determined during virtual-machine startup and
         * typically depends upon the locale and charset of the underlying
         * operating system.
         *
         * @return  A charset object for the default charset
         *
         * @since 1.5
         */
        public static Charset defaultCharset() {
            if (defaultCharset == null) {
            synchronized (Charset.class) {
            java.security.PrivilegedAction pa =
                new GetPropertyAction("file.encoding");
            String csn = (String)AccessController.doPrivileged(pa);
            Charset cs = lookup(csn);
            if (cs != null)
                defaultCharset = cs;
                    else 
                defaultCharset = forName("UTF-8");
                }
        }
        return defaultCharset;
        }
    

下面详细叙述一下字节流

一、InputStream 和 OutputStream 是两个 abstact 类,对于字节为导向的 stream 都扩展这两个鸡肋(基类 ^_^ ) ;

040832408755959

  1. FileInputStream,打开本地文件的流,常用,有3个构造方法
    public FileInputStream(File file)
    public FileInputStream(String name)
    public FileInputStream(FileDescriptor fdObj) 值得强调,这个构造是不能直接用的,FileDescriptor 相当于打开文件的句柄,可以用一个文件流创建另一个,这样创建的流相当于是一个。一个流关闭的话, 另一个也不能读取。
  2. PipedInputStream,必须与PipedOutputStream一起使用,必须是两个或者多个线程中使用,类似生产者消费者模型, PipedOutputStream将数据写到共享的buffer数组中,通知PipedInputStream读取。有两点注意事项:a) 使用PipedInputStream的read方法时候要注意,如果缓冲区没有数据的话,会阻塞当前线程,在主线程中运行的话,会卡住不动。b)PipedOutputStream所在的线程如果停止,那么PipedOutputStream所使用的资源也会回收,会造成pipe 的“broken”,PipedInputStream的read方法也会报错。“A pipe is said to be broken if a thread that was providing data bytes to the connected piped output stream is no longer alive. ”
  3. FilterInputStream,本身是不能被实例化的,是BufferedInputStream等的父类,其实不创建这个类也可以实现它的子类,这个类内部的方法几乎全部都是复用父类的方法。其实它存在的意义更多是代表一个抽象,意思是在InputStream的基础之上对返回数据进行了重新包装或者处理,处理原因可能各不相同,于是又了各不相同的子类。
  4. LineNumberInputStream,这个类是字节流和字符流转换中的失败产物,已经确定为被废弃,废弃的理由是在字节流中强制的判断读取换行,不考虑编码方面的问题。先不管功能能不能实现,首先从抽象层次上面就有欠缺。挪到字符流里面就皆大欢喜。对应的有LineNumberReader这个类可以使用。具体参见LineNumberReader详解。
  5. DataInputStream,直接读取目标文件的byte,拼接或转化byte为其他基本类型,比如下面方法
    public final int readInt() throws IOException {
            int ch1 = in.read();
            int ch2 = in.read();
            int ch3 = in.read();
            int ch4 = in.read();
            if ((ch1 | ch2 | ch3 | ch4) < 0)
                throw new EOFException();
            return ((ch1 << 24) + (ch2 << 16) + (ch3 << 8) + (ch4 << 0));
        }
    

    对于基本类型可以这样转化,但是对于float和double,各自用了Float类和Double类中的native方法进行转化,想来与操作系统底层有关系。

    public final double readDouble() throws IOException {
        return Double.longBitsToDouble(readLong());
        }
    

    唯一实现的比较复杂的是readUTF方法,需要读取全部数据,必须是符合格式的,需要用DataOutputStream的writeUTF进行对应的写。DataInputStream在实际运用中,还是应该与DataOutputStream一起使用,不然的话,意义不是十分大。

  6. BufferedInputStream,初始化一个8192大小的缓存,提高效率用,调用API上面没有任何不同,只是减少了直接读取系统数据的次数。内部持有一个普通的inputStream,只有缓冲区空了以后,才真正调用inputStream的read去写满缓冲区,所以直接用BufferedInputStream的read方法可以提高效率。
    有点意思的是这个类里面用了一个AtomicReferenceFieldUpdater对象来进行对volatile类型缓冲byte数组的更新和替换,这个类的compareAndSet方法带有原子性质的比较和更新。

    /**
         * Atomic updater to provide compareAndSet for buf. This is
         * necessary because closes can be asynchronous. We use nullness
         * of buf[] as primary indicator that this stream is closed. (The
         * "in" field is also nulled out on close.)
         */
        private static final 
            AtomicReferenceFieldUpdater<BufferedInputStream, byte[]> bufUpdater = 
            AtomicReferenceFieldUpdater.newUpdater
            (BufferedInputStream.class,  byte[].class, "buf");// 创建原子更新器
    ...
    /**
         * Fills the buffer with more data, taking into account
         * shuffling and other tricks for dealing with marks.
         * Assumes that it is being called by a synchronized method.
         * This method also assumes that all data has already been read in,
         * hence pos > count.
         */
        private void fill() throws IOException {
            byte[] buffer = getBufIfOpen();
        if (markpos < 0)
            pos = 0;        /* no mark: throw away the buffer */
        else if (pos >= buffer.length)    /* no room left in buffer */
            if (markpos > 0) {    /* can throw away early part of the buffer */
            int sz = pos - markpos;
            System.arraycopy(buffer, markpos, buffer, 0, sz);
            pos = sz;
            markpos = 0;
            } else if (buffer.length >= marklimit) {
            markpos = -1;    /* buffer got too big, invalidate mark */
            pos = 0;    /* drop buffer contents */
            } else {        /* grow buffer */
            int nsz = pos * 2;
            if (nsz > marklimit)
                nsz = marklimit;
            byte nbuf[] = new byte[nsz];
            System.arraycopy(buffer, 0, nbuf, 0, pos);
                    if (!bufUpdater.compareAndSet(this, buffer, nbuf)) {//进行更新比较, 如果buf对象和buffer相同, 那么进行更新,不同的话,不更新
                        // Can't replace buf if there was an async close.
                        // Note: This would need to be changed if fill()
                        // is ever made accessible to multiple threads.
                        // But for now, the only way CAS can fail is via close.
                        // assert buf == null;
                        throw new IOException("Stream closed");
                    }
                    buffer = nbuf;
            }
            count = pos;
        int n = getInIfOpen().read(buffer, pos, buffer.length - pos);
            if (n > 0)
                count = n + pos;
        }
    
  7. PushBackInputStream,特点是unread()方法,作用是在读取流的过程中自行添加入字节或者字节数组,进行重新读取,小说中随机插入的广告url倒是可以用这个实现,冷不丁的在读取过程中插入一个urlbyte数组,倒也方便。
  8. ByteArrayInputStream,特点是内存操作,读取的数据全部都在缓存数组中,构造方法如下
    public ByteArrayInputStream(byte buf[])
    public ByteArrayInputStream(byte buf[], int offset, int length)
    
  9. StringBufferInputStream,这个类已经被废弃,原因是错误的对字节流进行向字符流的转化,忽略了编码问题。值得一提的是, 这个类里基本所有部分方法都是线程安全的。swing的某个类中还引用了这个方法。
  10. ObjectInputStream,这个类可以说的比较多
    1. 实现了两个接口,ObjectInut:定义了可以read到的类型,ObjectStreamConstants:定义了读取文件类型的常量,使用readObject时候,区分读取到的对象是什么类型,从序列化的对象进行读取时候,需要通过标志位来判断读取到的是什么对象,这个常量里面定义了这些值, 都是short的。
    2. 拥有一个内部类BlockDataInputStream,这个类的作用是读取基本类型数据时候进行缓存,以提高效率,但是也产生了问题,http://www.tuicool.com/articles/v6RNNr 反序列化和序列化一定注意,建议使用read(byte[],start,end) 替代简单的read(byte[]),使用后者的话, 可能出现读取乱码,内容错误等问题,尤其是音视频, 可能出现杂音,因为ObjectInputStream是根据单个字节来判断数据类型的,所以一定要准确。
  1. OutputStream, 基本每个InputStream都有一个对应的OutputStream,来实现对应的功能,基本全都是抽象方法。
    1. FileOutputStream,FileDescriptor相当于句柄, 既然是句柄, 就会有多个流可能使用之, 所以FileDescriptor有incrementAndGetUseCount方法, 用来线程安全的进行引用计数器+1的操作。另外值得注意的是,FileOutputStream还有追加写入的构造方法
      public FileOutputStream(File file, boolean append)
              throws FileNotFoundException
          {
              String name = (file != null ? file.getPath() : null);
          SecurityManager security = System.getSecurityManager();
          if (security != null) {
              security.checkWrite(name);
          }
              if (name == null) {
                  throw new NullPointerException();
              }
          fd = new FileDescriptor();
              fd.incrementAndGetUseCount();
              this.append = append;
          if (append) {
              openAppend(name);
          } else {
              open(name);
          }
          }
      
    2. PipedOutputStream,需要与InputStream进行配合使用,不在赘述

(转)Flashbuilder4.5中文版破解方法

具体步骤如下:

1.到Adobe官网上下载FlashBuilder_4_5_LS10.exe,有简体中文版的;

2.安装时,不用序列号,直接选择安装试用版;

3.安装完成后依次修改下列3个文件:

(1).plugins\com.adobe.flexbuilder.project_4.5.1.313231\META-INF下面的MANIFEST.MF修改:

Bundle-Version: 0.0.0

(2).features\com.adobe.flexide.feature_4.5.1.313231下面的feature.xml修改:

<plugin
id=”com.adobe.flexbuilder.project”
download-size=”0″
install-size=”0″
version=”0.0.0″/>

(3).plugins\com.adobe.flexbuilder.flex_4.5.1.313231下面:

复制config.xml并重命名为config_builder.xml

修改完成之后,再启动Adobe Flash Builder 4.5,破解成功!

【Android】PA4D_CH7 文件、保存状态和首选项

本章主要知识点如下:

  • 了解Shared Preferences
  • 接口OnSharedPreferenceChangeListener, 每当添加、移除或者修改一个特定的Shared Preference 时候可以调用一个回调函数
  • 创建Preference Screen(首选项框架)
    • PreferenceActivity,  适用于API Level 11 (SDK3.0) 以前(xml 配置)
    • PreferenceFragment, 适用于API Level 11 (SKD3.0) 以后,兼容平板 (preserence-header配置)
    • Activity中根据当前版本号动态配置二者
    • Preference Screen中,  使用了ListPreference控件 , 默认保存的是String类型的值
    • 可以继承Preference Screen中的组件, 并且重写组件,实现各种功能,比如在设置中选择字体的颜色
  • 在Activity销毁或者隐藏以后,保存Fragment的状态
  • 添加静态文件作为资源
  • 使用Environment获取目录

【转】JVM 堆最大能调多大?

原文链接:http://blog.csdn.net/antswallow/article/details/5898604

 

上次用weblogic 把 -XmxXXXX 设成2G,就启动不起来,设小点就起来了,当时很气,怎么2G都起不了,今天在看到了一篇解释,转过来了
这 次一位老友提出了这个问题,记得当年一个java高手在blogjava提出后,被骂得半死。大家使用java -XmxXXXX -version版本得出了不同的结论。后来老友说大概是1800M左右,我当时反驳,“我设置过服务器8G内存,我使用两个tomcat,每个2G”。 为此,我翻开所有的JVM的内存管理的c代码,没有任何结论。我不是linux内核程序员,但是我看过linux的源码,知道32位体系结构的计算机寻址 空间是2^32=4G,intel Pentium Pro处理器寻址空间是36位,CPU内部增加了PAE寄存器。用于处理多出来的4根地址
线 的使用,所以PAE的技术实现最大2^36=64G寻址。通过linux的内核源码,标准Linux内核对于物理内存的管理采用1:3的分配比例,即物理 内存的1/4为内核空间(kernel space),剩下的3/4为用户进程空间(user space),因此,在一台4G内存的服务器上,用户进程可使用的内存最大也就是3G。当进程被内核调入CPU运行时,不同的地址空间数据会被调入4G以 内的用户进程空间,其实就能用3G。 IA32架构上,单一进程是不能使用超过4G的内存空间的。但是我记得我给mysql server分配内存大约是1.7G左右,不是2的32次方-1,我分配java 2G内存的计算机是IBM的RS6000.
经过不同平台的测 试,我得出了大概的数值,win2k下1.6G左右,nt下1.2G,原因是这样的,Classic VM and HotSpot VM 存放用户区的连续地址中,NT把 kernel DLLs 放在 0x7c 开头的地址空间,所以nt下只有<2G的空间,所以JVM heap 使用极限是2G.用户的dll开始于0x77000000,用户的应用程序开始于0x00400000.我现在唯一确定的是sun可能为了防止和某些 JVM插件的冲突,把dll的地址给rebase一下,这样使用的空间就很少了一部分.为什末rebase,原因是这样的,因为在windows下编译 dll 的默认地址都是10000000, 一般在release之前的时候要rebase一下,rebase 的 -b 这个参数是指定一个起始地址,MSDN建议地址是0x60000000,这个工具随visual studio和platform SDK发放。
例 如
rebase.exe -b 0x6D000000 /jdk/jre/bin/*.dll /jdk/jre/bin/hotspot/jvm.dll这样你的JVM用的内存多一些,目前关于这个我只能得到BEA的 JRockit最大也只能使用1.8G内存,看来各家编译JDK时都作了些手脚.
目前只能得到bea的的-Xmx最小值是16 MB,sun的资料很不全,还好java开源了,可以不依靠sun了.

sun提供的资料
Maximum Address Space Per Process

Operating System                   Maximum Address Space Per Process

Redhat Linux 32 bit                                      2 GB

Redhat Linux  64 bit                                     3 GB

Windows 98/2000/NT/Me/XP                                  2 GB

Solaris x86 (32 bit)                                      4 GB

Solaris 32 bit                                            4 GB

Solaris 64 bit                                            Terabytes
以 上文档有误,32位的redhat Server利用 Highmem技术可以使用3G内存.
solaris不愧是java的诞生平台。

问了一下bea的工程师,得出大致的结论,
Windows 2003/XP using the /3GB switch (32-bit OS)
1.85 GB – JRockit 5.0 R25.2 (SP2)
2.85 GB – JRockit 5.0 R26 (SP3)

Windows 2003/XP x64 Edition with a 32-bit JVM (64-bit OS)
2.05 GB – JRockit 5.0 R25.2 (SP2)
3.85 GB – JRockit 5.0 R26 (SP3)

对于windows 2000打开3G模式,windows核心编程说得很清楚,boot.ini加入/3G参数。

[boot loader]
timeout=30
default=multi(0)disk(0)rdisk(0)partition(2)/WINNT
[operating systems]
multi(0)disk(0)rdisk(0)partition(2)/WINNT=”????” /3GB

Note: “????” in the previous example can be the programmatic name of any of the following operating system versions:

Windows XP Professional
Windows Server 2003
Windows Server 2003, Enterprise Edition
Windows Server 2003, Datacenter Edition
Windows 2000 Advanced Server
Windows 2000 Datacenter Server
Windows NT Server 4.0, Enterprise Edition

在我的机子测试一把,我的自己配置,1G内存,winXP

没有打开3G模式,sun的jdk 1.6 java -Xmx1447M -version,揪出错了,jrockit-R27.1.0-jdk1.5.0_08为1911M,3G模式 sun的jdk没有变化,IBM J9 VM (build 2.3, J2RE 1.5.0 IBM J9 2.3 Windows XP x86-32 j9vmwi3223-2006050
4 (JIT enabled) 3G和2G相同,java -Xmx1787M -version 就出问题,jrockit-R27.1.0-jdk1.5.0_08为2899M,注意Xmx的内存不是物 理内存,我的机子物理内存只有1G
jrockit不愧为java第一虚拟机,只可惜不开源。

今天分析了当前比较流行的几个不同公司不同版本JVM的最大内存,得出来的结果如下:

公司 JVM版本 最大内存(兆)client 最大内存(兆)server
SUN 1.5.x 1492 1520
SUN 1.5.5(Linux) 2634 2660
SUN 1.4.2 1564 1564
SUN 1.4.2(Linux) 1900 1260
IBM 1.4.2(Linux) 2047 N/A
BEA JRockit 1.5 (U3) 1909 1902

除非特别说明,否则JVM版本都运行在Windows操作系统下

附:如何获得JVM的最大可用内存 

在命令行下用 java -Xmx1200m -XX:MaxPermSize=60m -version 命令来进行测试,然后逐渐的增大XXXX的值,如果执行正常就表示指定的内存大小可用,否则会打印错误信息。
最后得到的虚拟机实际分配到的
总内存大小=堆内存+非堆内存
1200m:为堆内存大小,如果不指定后者参数则有最大数限制,网上很多文章认为这就是JVM内存,-Xmx为设置最大堆内存
60m:为非堆内存大小,-XX:MaxPermSize实为永久域内存,在堆内存之外,属于非堆内存部分,jdk1.5我测了好像默认为62m,即得到非堆部分默认内存)

Sun HotSpot 1.4.1使用分代收集器,它把堆分为三个主要的域:新域、旧域以及永久域。Sun JVM生成的所有新对象放在新域中。一旦对象经历了一定数量的垃圾收集循环后,便获得使用期并进入旧域。在永久域中Sun JVM则存储class和method对象。就配置而言,永久域是一个独立域并且不认为是堆的一部分。

实际发现版本上有细微差别的JDK最大容许内存值都不尽相同,因此在实际的应用中还是要自己试验一下看到底内存能达到什么样的值。

通过这个表想说明的是,如果你的机器的内存太多的话,只能通过多运行几个实例来提供机器的利用率了,例如跑Tomcat,你可以多装几 个Tomcat并 做集群,依此类推。

≡≡≡ 网友评论 ≡≡≡
东子 网友说:
Windows下的最大内存应该跟NT内核对地址空间的保留也有关系, 好像默认情况下NT内核要占用高2G的地址空间, 所以应用程序撑死能获得的内存不会超过2G; 记得有一个参数可以让NT只占1G内存, 这样应用程序就有3G地址空间可用, 相应环境下JVM能允许的最大内存可能也会升高.

at 05-10-06 00:04
purpureleaf 网友说:
windows的每个应用(不是寻址)的寻址空间一般是2g或者3g,取决于一个参数。但是只要使用一组特定的函数分配内存,每个应用的寻址空间可以远远 超过4g

jdk可能是设置不了那个大的内存,但那不是windows造成的,是jdk造成的,在linux上一样设置不了。看来做java的朋友对windows 还是不熟

代码整洁之道

  • 命名,多花些时间推敲命名, 有意义的命名非常重要。
    • 接口的命名,不使用“I”开头比较简洁,加上I以后是比较规范,但是比较繁琐以及废话。如果想区别接口和实现,不如在实现类中进行编码,比如添加后缀“Imp”,android以及jdk中的大多数接口都没有使用I。
    • 取名字带有简写要慎重, 比如“人事系统”的类, 前面都是“RSXT。。”,除了让快捷按钮找不到类以外,没有啥意义了,用包吧。
  • 函数,函数要短小,要职责明确,最好功能单一,参数最好不要超过两个,否则有顺序问题。
    • 合理的返回值可以使函数更加明了。
    • 封装Switch语句。
  • 注释,注释里面就有一堆问题要说了
    • 删除不必要的作者修改痕迹,这种东西应该留给源代码管理软件。
    • 删除你要准备注释掉,但是不想删的东西,他们丢不了,我担保。
    • 不要为了写注释而写注释。
    • 代码中令人费解和取巧的地方,甚至可以单独留作一个方法,强调注释,比如“开发取迭代次数”的例子
  • 格式,代码都是从左往右,从上往下读的
    • 空行代表全新的概念
    • 成员变量注释要慎重,影响可读性
  • 对象和数据结构
    • 过程式代码(使用数据结构)便于在不改动既有数据结构的前提下添加新函数,面向对象代码便于在不改动既有函数的前提下添加新类,反过来讲,过程式代码难以添加新数据结构,因为必须修改所有函数。面向对象代码难以添加新函数,因为必须修改所有类
    • 火车失事类型代码 a.getb().getc().getd()……这种代码是否违反德墨忒尔律,取决于b,c,d是数据结构还是对象,如果是对象,那么内部结构应该保持隐藏,这样做自然违反。
  • 错误处理,雅致的使用错误处理的思路
    • 使用抛出异常来使程序逻辑和验证错误代码隔离。
    • 使用不可控异常,一旦一底层函数声明抛出异常, 那么上层函数逐级都要修改。。。。
    • 异常要说明,堆栈不足以说明一切
    • 打包调用API,减小对厂商API的依赖,将外部API打包调用在自己的类中,这样只有一处调用,自然好修改。
    • 别返回null,用特例模式,创建一个类或者配置一个对象,用来处理特例,空对象?
    • 别传递null值。断言验证null