`
lzy.je
  • 浏览: 148608 次
  • 性别: Icon_minigender_1
  • 来自: 沈阳
社区版块
存档分类
最新评论

JRE Hack 浅度研究

阅读更多

          很多时候对应用软件代码层面的性能调优受到很多主观、客观条件的影响,本文所述的 JRE Hack 就是在这样的背景下展开的。当前的应用中需要记录大量的调试信息,程序直接采用了 System.out.println 方法来将这些内容输出到日志文件中(WebSphere 的 outputStreamRedirect 默认配置到了 ${SERVER_LOG_ROOT}/SystemOut.log 文件),因此 System.out.println 方法大量遍布在整个程序代码中。而由于该方法是线程安全的,即被同步(临界区保护)形成了串行执行,结果造成整体业务处理串行点很多,性能比较低下。解决 方法也很简单,就是避免使用 System.out.println 方法来进行日志输出减少串行瓶颈,可换用支持异步输出的 log4j 组件来实现日志功能。本来这个事情通过代码批量替换就可以完成,但实际诸多原因造成不允许修改应用代码,那么该怎么解决这个问题呢?

          这样的前提就不得以造就这样一个方法,通过把 JRE 中提供的 System.out.println 方法实现代码替换成使用 log4j 来进行 Log 输出的实现代码。这里并不打算评论该方法的好坏(实际上 JRE 中使用了 PrintStream 类的地方不少,该方法影响面较大),本文姑且作为对 JRE 内部粗浅研究的记录吧。

          应用使用的是 IBM JRE 5.0,JRE 本身是开源的。用 DJ Java Decompiler 工具也可以反编译得到相关 Java 类源码(System.class、System$1.class 位于 jre/lib/vm.jar 中)。找到 java/lang/System.java 文件,可以看到 stdin、stdout、stderr 都是 PrintStream 类实例:

 

// Typically, these are connected to the shell which
// ran the Java program.
/**
 * Default input stream
 */
public static final InputStream in;
/**
 * Default output stream
 */
public static final PrintStream out;
/**
 * Default error output stream
 */
public static final PrintStream err;

 

          在 java/io/PrintStream.java(PrintStream.class 位于 jre/lib/core.jar 中)文件中可以看到 println(String) 等相关方法定义如下:

 

/**
 * Print a String and then terminate the line.  This method behaves as
 * though it invokes <code>{@link #print(String)}</code> and then
 * <code>{@link #println()}</code>.
 *
 * @param x  The <code>String</code> to be printed.
 */
public void println(String x) {
	synchronized (this) {
		print(x);
		newLine();
	}
}

/**
 * Print a string.  If the argument is <code>null</code> then the string
 * <code>"null"</code> is printed.  Otherwise, the string's characters are
 * converted into bytes according to the platform's default character
 * encoding, and these bytes are written in exactly the manner of the
 * <code>{@link #write(int)}</code> method.
 *
 * @param  s   The <code>String</code> to be printed
 */
public void print(String s) {
	if (s == null) {
		s = "null";
	}
	write(s);
}

private void write(String s) {
	try {
	synchronized (this) {
		ensureOpen();
		textOut.write(s);
		textOut.flushBuffer();
		charOut.flushBuffer();
		if (autoFlush && (s.indexOf('\n') >= 0))
		out.flush();
	}
	}
	catch (InterruptedIOException x) {
	Thread.currentThread().interrupt();
	}
	catch (IOException x) {
	trouble = true;
	}
}

 

          此时,如果 hack 代码很简单的话,那么就可以将其直接写入到 PrintStream.java 相关方法中。但这样一来,加入的 hack 代码会与 JRE 中这些 Java 标准类库代码混在一起。更多时候我们为了便于维护,有必要将这些 hack 代码放在另外的独立 jar 文件中,这会引出一些问题,下面慢慢道来。

          让我们先再看看 System 类的 completeInitialization 方法代码,注意 Sun JRE 中 System 类实现并没有该方法:

 

static void completeInitialization() {
	setIn(com.ibm.JVM.io.ConsoleInputStream.localize(new BufferedInputStream(new FileInputStream(FileDescriptor.in))));
	Terminator.setup();
	// initialize the getName cache
	Class.setClassNameMap();
	com.ibm.misc.SystemIntialization.lastChanceHook();
}
 

          对于上面提出的需求,可以在这里直接加入我们 hack 的 PrintStream 实现:

 

import com.lzy.javaeye.fooattach.FooPrintStream;

... ...

static void completeInitialization() {
	setIn(com.ibm.JVM.io.ConsoleInputStream.localize(new BufferedInputStream(new FileInputStream(FileDescriptor.in))));

	// Hack code
	setOut(new FooPrintStream(out, true));
	setErr(new FooPrintStream(err, true));
	// Hack code end

	Terminator.setup();
	// initialize the getName cache
	Class.setClassNameMap();
	com.ibm.misc.SystemIntialization.lastChanceHook();
}

... ...

 

          这里使用的 com.lzy.javaeye.fooattach.FooPrintStream 类被单独封装在了 FooPrintStream.jar 文件中,实现代码如下:

 

package com.lzy.javaeye.fooattach;

import java.io.PrintStream;
import java.io.OutputStream;

public final class FooPrintStream extends PrintStream {

    public FooPrintStream(OutputStream outputstream, boolean flag)
    {
        super(outputstream, flag);
        
        ps = new PrintStream(outputstream, flag);
    }

    public void println(String s)
    {
    	ps.println(s + " - [length: " + s.length() + "]");
    }
    
    private PrintStream ps;
}

 

          这里仅用于说明,为了简便其中的 println 方法并没有直正调用 log4j 做 Log 输出,而是简单的在输出 String 加上了长度信息。

 

          接下来把 FooAttach.jar 文件放入 JRE HOME 的 lib 目录中,该目录中有 vm.jar 和 core.jar 等一系列“核心”的 JRE class jar 文件。下面我们就可以将这个 hack 版的 System.java 文件编译了:

 

javac –cp %CLASSPATH%\FooAttach.jar System.java

 

          此时会生成 System.class 和 System$1.class(System.AccessController.doPrivileged 方法使用的匿名类)两个文件,将把这个编译好的“新” System 类放入到 jar/lib/vm.jar/java/lang 目录中。这里随便写个测试程序:

 

public class PrintTest {
	public static void main(String[] args) {
		System.out.println("test11");
	}
}

 

          心急的兄弟如果现在运行它,那会得到 NoClassDefFoundError 异常:

 

Exception in thread "main" java/lang/NoClassDefFoundError: com.lzy.javaeye.fooattach.FooPrintStream
at java/lang/System.completeInitialization (System.java:117)
at java/lang/Thread.<init> (Thread.java:124)
JVMJ9VM015W Initialization error for library jclscar_23(14): JVMJ9VM009E J9VMDllMain failed

 

          创建 Java 虚拟机失败的原因在于 vm.jar 是被 bootstrap class loader 加载的,其中的 System 类当被 bootstrap class loader 加载时由于找不到依赖的 FooAttach.jar 包,造成抛出 FooPrintStream 类未定义异常,最终导致整个 JVM 初使化失败。

          下面是些关于 Java 类加载方面的科普知识,理解的兄弟请自行快速跳过。

 

          关于 Java 类是如何被加载的,可参见Sun提供的一篇文章:
          How Classes are Found

          还有一篇 IBM 写的关于类加载器相关问题诊断的文篇,也很好的说明了类加载问题:
          Demystifying class loading problems, Part 1: An introduction to class loading and debugging tools

          这里列出 Java 命令行 classpath 相关选项的说明:

 

command-line_classpath_options

 

          我们也可以通过 JVM heap dump 分析得到类加载的相关信息。在下面的示例 heap dump 中可以看到 3 个类加载及其加载的类信息:

 

本质上有以下 4 种生成 Java heap dump 的事件:

* A fatal native exception occurs;
* The JVM runs out of heap space;
* A signal is sent to the JVM (for example, if Control-Break is pressed on Windows, or Control-\ on Linux);
* The com.ibm.JVM.Dump.JavaDump() method is called.

 

 

heapdump_classloader_sample

 

          其中显示的 3 个类加载器分别是:

 

  1. The system class loader (sun/misc/Launcher$AppClass loader);
  2. The extension class loader (sun/misc/Launcher$ExtClass loader);
  3. The bootstrap class loader (*System*).


          上面已经说明,发生 NoClassDefFoundError 异常并导致 JVM 初使化失败的原因是由于我们生成的 FooAttach.jar 包没有被 bootstrap class loader 加载造成的。那么我们只需要通过 Java 的 -Xbootclasspath 启动选项将 FooAttach.jar 包含进 bootstrap class loader 加载列表即可:(C:\Program Files\Java_ibm1.5\jre 是我本机环境的 JRE HOME)

 

-Xbootclasspath:”C:\Program Files\Java_ibm1.5\jre\lib\FooAttach.jar;C:\Program Files\Java_ibm1.5\jre\lib\vm.jar;C:\Program Files\Java_ibm1.5\jre\lib\core.jar;C:\Program Files\Java_ibm1.5\jre\lib\charsets.jar;C:\Program Files\Java_ibm1.5\jre\lib\graphics.jar;C:\Program Files\Java_ibm1.5\jre\lib\security.jar;C:\Program Files\Java_ibm1.5\jre\lib\ibmpkcs.jar;C:\Program Files\Java_ibm1.5\jre\lib\ibmorb.jar;C:\Program Files\Java_ibm1.5\jre\lib\ibmcfw.jar;C:\Program Files\Java_ibm1.5\jre\lib\ibmorbapi.jar;C:\Program Files\Java_ibm1.5\jre\lib\ibmjcefw.jar;C:\Program Files\Java_ibm1.5\jre\lib\ibmjgssprovider.jar;C:\Program Files\Java_ibm1.5\jre\lib\ibmjsseprovider2.jar;C:\Program Files\Java_ibm1.5\jre\lib\ibmjaaslm.jar;C:\Program Files\Java_ibm1.5\jre\lib\ibmjaasactivelm.jar;C:\Program Files\Java_ibm1.5\jre\lib\ibmcertpathprovider.jar;C:\Program Files\Java_ibm1.5\jre\lib\server.jar;C:\Program Files\Java_ibm1.5\jre\lib\xml.jar”

 

          此时添加了上述 -Xbootclasspath 选项后再运行测试程序就可以得到正确结果了。尝试加入 System.getProperties().list(System.out); 代码,我们可以看到当前 JVM 系统属性信息:

 

java.assistive => ON
java.runtime.name => Java(TM) 2 Runtime Environment, Standard Edition
ibm.signalhandling.rs => false
sun.boot.library.path => C:\Program Files\Java_ibm1.5\jre\bin
java.vm.version => 2.3
com.ibm.oti.configuration => scar
java.vm.vendor => IBM Corporation
java.vendor.url => http://www.ibm.com/
path.separator => ;
java.vm.name => IBM J9 VM
user.country => CN
java.vm.specification.name => Java Virtual Machine Specification
user.dir => C:\Documents and Settings\Administrator\My Documents\workspace\PrintTest
java.runtime.version => 2.3
java.fullversion => J2RE 1.5.0 IBM J9 2.3 Windows XP x86-32 j9vmwi3223-20060504 (JIT enabled)
J9VM - 20060501_06428_lHdSMR
JIT - 20060428_1800_r8
GC - 20060501_AA
java.awt.graphicsenv => sun.awt.Win32GraphicsEnvironment
os.arch => x86
com.ibm.vm.bitmode => 32
java.io.tmpdir => C:\DOCUME~1\ADMINI~1\LOCALS~1\Temp\
line.separator =>

com.ibm.util.extralibs.properties =>
java.vm.specification.vendor => Sun Microsystems Inc.
user.variant =>
java.awt.fonts =>
os.name => Windows XP
sun.java2d.fontpath =>
sun.jnu.encoding => GB18030
java.library.path => C:\Program Files\Java_ibm1.5\jre\bin;.;C:\Program Files\Java_ibm1.5\jre\bin;C:/Program Files/Java/jre6/bin/client;C:/Program Files/Java/jre6/bin;C:\Program Files\Common Files\NetSarang;C:\Program Files\ruby-1.8.7-p72\bin;C:\oracle\product\10.2.0\client\bin;C:\WINDOWS\system32;C:\WINDOWS;C:\WINDOWS\System32\Wbem;C:\Program Files\TortoiseSVN\bin;C:\Program Files\JProbe 8.1\bin;C:\Program Files\IBM\Rational AppScan\;C:\Program Files\MinGW\bin;C:\Program Files\StormII\Codec;C:\Program Files\StormII;C:\Program Files\UltraEdit\
jxe.current.romimage.version => 9
com.ibm.oti.vm.bootstrap.library.path => C:\Program Files\Java_ibm1.5\jre\bin
com.ibm.cpu.endian => little
java.specification.name => Java Platform API Specification
java.class.version => 49.0
ibm.system.encoding => GB18030
java.util.prefs.PreferencesFactory => java.util.prefs.WindowsPreferencesFactory
invokedviajava =>
os.version => 5.1 build 2600 Service Pack 3
com.ibm.oti.vm.library.version => 23
user.home => C:\Documents and Settings\Administrator
user.timezone =>
java.awt.printerjob => sun.awt.windows.WPrinterJob
file.encoding => GBK
java.specification.version => 1.5
user.name => Administrator
java.class.path => C:\Documents and Settings\Administrator\My Documents\workspace\PrintTest\bin
java.vm.specification.version => 1.0
sun.arch.data.model => 32
java.home => C:\Program Files\Java_ibm1.5\jre
com.ibm.oti.jcl.build => 20060331_1751
user.language => zh
ibm.signalhandling.sigint => true
java.specification.vendor => Sun Microsystems Inc.
os.encoding => GB18030
awt.toolkit => sun.awt.windows.WToolkit
java.vm.info => J2RE 1.5.0 IBM J9 2.3 Windows XP x86-32 j9vmwi3223-20060504 (JIT enabled)
J9VM - 20060501_06428_lHdSMR
JIT - 20060428_1800_r8
GC - 20060501_AA
java.version => 1.5.0
java.ext.dirs => C:\Program Files\Java_ibm1.5\jre\lib\ext
jxe.lowest.romimage.version => 9
sun.boot.class.path => C:\Program Files\Java_ibm1.5\jre\lib\FooAttach.jar;C:\Program Files\Java_ibm1.5\jre\lib\vm.jar;C:\Program Files\Java_ibm1.5\jre\lib\core.jar;C:\Program Files\Java_ibm1.5\jre\lib\charsets.jar;C:\Program Files\Java_ibm1.5\jre\lib\graphics.jar;C:\Program Files\Java_ibm1.5\jre\lib\security.jar;C:\Program Files\Java_ibm1.5\jre\lib\ibmpkcs.jar;C:\Program Files\Java_ibm1.5\jre\lib\ibmorb.jar;C:\Program Files\Java_ibm1.5\jre\lib\ibmcfw.jar;C:\Program Files\Java_ibm1.5\jre\lib\ibmorbapi.jar;C:\Program Files\Java_ibm1.5\jre\lib\ibmjcefw.jar;C:\Program Files\Java_ibm1.5\jre\lib\ibmjgssprovider.jar;C:\Program Files\Java_ibm1.5\jre\lib\ibmjsseprovider2.jar;C:\Program Files\Java_ibm1.5\jre\lib\ibmjaaslm.jar;C:\Program Files\Java_ibm1.5\jre\lib\ibmjaasactivelm.jar;C:\Program Files\Java_ibm1.5\jre\lib\ibmcertpathprovider.jar;C:\Program Files\Java_ibm1.5\jre\lib\server.jar;C:\Program Files\Java_ibm1.5\jre\lib\xml.jar
java.vendor => IBM Corporation
file.separator => \
java.compiler => j9jit23
sun.io.unicode.encoding => UnicodeLittle
ibm.signalhandling.sigchain => true

 

          其中的 sun.boot.class.path 系统属性给出了当前配置的所有 boot class path,这些 jar 包会由 Bootstrap class loader 负责将其中的类装入 JVM。

          截止到此,我们最开始的需求“基本”已经可以达到了,即在不改变应用代码中对 System.out.println 方法调用前提下,通过 hack JRE 实现改变了 System.out.println 实现。但所谓“基本”是由于我们需要配置 -Xbootclasspath 选项来让 bootstrap class loader 加载我们封装的自定义代码 jar 文件。那么是否也可以通过上面类似的 hack 方法来更新 JVM 的默认 boot class path 值呢?经过分析认为不可能,因为 boot class path 的内容,也就是 bootstrap class loader 负责加载的默认 jar 文件集是 JVM 在源代码中/编译前指定的,而在 JRE 层面完成该配置的方法只能通过 Java 进程的 -Xbootclasspath 命令行参数来完成。

          下面的分析过程将针对 JVM 实现展开,目标是以 openjdk 开源项目为基础,源码可以在这里下载:
          openjdk-7-ea-src-b59-14_may_2009

 

          首先,openjdk\hotspot\src\share\vm\runtime\os.cpp 文件 bool os::set_boot_path(char fileSep, char pathSep) 方法指出了 bootstrap class loader 负责加载的默认 jar 文件集:

 

bool os::set_boot_path(char fileSep, char pathSep) {
    const char* home = Arguments::get_java_home();
    int home_len = (int)strlen(home);

    static const char* meta_index_dir_format = "%/lib/";
    static const char* meta_index_format = "%/lib/meta-index";
    char* meta_index = format_boot_path(meta_index_format, home, home_len, fileSep, pathSep);
    if (meta_index == NULL) return false;
    char* meta_index_dir = format_boot_path(meta_index_dir_format, home, home_len, fileSep, pathSep);
    if (meta_index_dir == NULL) return false;
    Arguments::set_meta_index_path(meta_index, meta_index_dir);

    // Any modification to the JAR-file list, for the boot classpath must be
    // aligned with install/install/make/common/Pack.gmk. Note: boot class
    // path class JARs, are stripped for StackMapTable to reduce download size.
    static const char classpath_format[] =
        "%/lib/resources.jar:"
        "%/lib/rt.jar:"
        "%/lib/sunrsasign.jar:"
        "%/lib/jsse.jar:"
        "%/lib/jce.jar:"
        "%/lib/charsets.jar:"
        "%/classes";
    char* sysclasspath = format_boot_path(classpath_format, home, home_len, fileSep, pathSep);
    if (sysclasspath == NULL) return false;
    Arguments::set_sysclasspath(sysclasspath);

    return true;
}

 

          然后,在 openjdk\hotspot\src\share\vm\runtime\arguments.hpp 文件中定义了 Arguments::set_sysclasspath 方法,它将参数给出的 jar 包文件路径存入 Arguments::_sun_boot_class_path 的 SystemProperty 结构中:

 

static void Arguments::set_sysclasspath(char *value) { _sun_boot_class_path->set_value(value); }

static SystemProperty *_sun_boot_class_path;

 

          其次,我们通过 Java 进程的 -Xbootclasspath 命令行参数配置的 boot class jar 包路径会通过 openjdk\hotspot\src\share\vm\prims\jvmtiEnv.cpp 文件中定义的 AddToBootstrapClassLoaderSearch 方法添加到上述的 _sun_boot_class_path SystemProperty 结构中。其中也是通过调用 Arguments::append_sysclasspath 方法来完成的:

 

JvmtiEnv::AddToBootstrapClassLoaderSearch(const char* segment) {
  jvmtiPhase phase = get_phase();
  if (phase == JVMTI_PHASE_ONLOAD) {
    Arguments::append_sysclasspath(segment);
    return JVMTI_ERROR_NONE;
  } else {
    assert(phase == JVMTI_PHASE_LIVE, "sanity check");

    // create the zip entry
    ClassPathZipEntry* zip_entry = ClassLoader::create_class_path_zip_entry(segment);
    if (zip_entry == NULL) {
      return JVMTI_ERROR_ILLEGAL_ARGUMENT;
    }

    // lock the loader
    Thread* thread = Thread::current();
    HandleMark hm;
    Handle loader_lock = Handle(thread, SystemDictionary::system_loader_lock());

    ObjectLocker ol(loader_lock, thread);

    // add the jar file to the bootclasspath
    if (TraceClassLoading) {
      tty->print_cr("[Opened %s]", zip_entry->name());
    }
    ClassLoader::add_to_list(zip_entry);
    return JVMTI_ERROR_NONE;
  }

}

 

<function id="AddToBootstrapClassLoaderSearch" jkernel="yes" phase="onload" num="149">
	<synopsis>Add To Bootstrap Class Loader Search</synopsis>
	<description>
		This function can be used to cause instrumentation classes to be defined by the
		bootstrap class loader. See
		<vmspeclink id="ConstantPool.doc.html#79383"
			name="Loading Using the Bootstrap Class Loader"
		preposition="in"/>.
		After the bootstrap
		class loader unsuccessfully searches for a class, the specified platform-dependent
	search path <paramlink id="segment"/> will be searched as well. Only one segment may be specified in
the <paramlink id="segment"/>. This function may be called multiple times to add multiple segments,
the segments will be searched in the order that this function was called.
<p/>
In the <code>OnLoad</code> phase the function may be used to specify any platform-dependent
search path segment to be searched after the bootstrap class loader unsuccessfully searches
for a class. The segment is typically a directory or JAR file.
<p/>
In the live phase the <paramlink id="segment"/> may be used to specify any platform-dependent
path to a <externallink id="http://java.sun.com/javase/6/docs/guide/jar/jar.html">
JAR file</externallink>. The agent should take care that the JAR file does not
contain any classes or resources other than those to be defined by the bootstrap
class loader for the purposes of instrumentation.
<p/>
The <vmspeclink/> specifies that a subsequent attempt to resolve a symbolic
reference that the Java virtual machine has previously unsuccessfully attempted
to resolve always fails with the same error that was thrown as a result of the
initial resolution attempt. Consequently, if the JAR file contains an entry
that corresponds to a class for which the Java virtual machine has
unsuccessfully attempted to resolve a reference, then subsequent attempts to
resolve that reference will fail with the same error as the initial attempt.
</description>
<origin>new</origin>
<capabilities>
</capabilities>
<parameters>
	<param id="segment">
		<inbuf>
			<char/>
		</inbuf>
		<description>
			The platform-dependent search path segment, encoded as a
			<internallink id="mUTF">modified UTF-8</internallink> string.
		</description>
	</param>
</parameters>
<errors>
	<error id="JVMTI_ERROR_ILLEGAL_ARGUMENT">
	<paramlink id="segment"/> is an invalid path. In the live phase, anything other than an
	existing JAR file is an invalid path.
</error>
</errors>
</function>

 

          Arguments::append_sysclasspath 方法被定义为:

 

static void Arguments::append_sysclasspath(const char *value) {
  _sun_boot_class_path->append_value(value);
}

 

          说明最终 bootstrap class loader 负责加载的 boot class jar 文件路径都被保存在 _sun_boot_class_path 中。

          接下来,openjdk\hotspot\src\share\vm\classfile\classLoader.cpp 文件中定义了 ClassLoader 类,它将 Arguments::_sun_boot_class_path 中指定的 jar 文件路径初使化,并保存在 ClassPathEntry 链表中:

 

void ClassLoader::setup_bootstrap_search_path() {
  assert(_first_entry == NULL, "should not setup bootstrap class search path twice");
  char* sys_class_path = os::strdup(Arguments::get_sysclasspath());
  if (TraceClassLoading && Verbose) {
    tty->print_cr("[Bootstrap loader class path=%s]", sys_class_path);
  }

  int len = (int)strlen(sys_class_path);
  int end = 0;

  // Iterate over class path entries
  for (int start = 0; start < len; start = end) {
    while (sys_class_path[end] && sys_class_path[end] != os::path_separator()[0]) {
      end++;
    }
    char* path = NEW_C_HEAP_ARRAY(char, end-start+1);
    strncpy(path, &sys_class_path[start], end-start);
    path[end-start] = '\0';
    update_class_path_entry_list(path, false);
    FREE_C_HEAP_ARRAY(char, path);
    while (sys_class_path[end] == os::path_separator()[0]) {
      end++;
    }
  }
}

 

ClassPathEntry* ClassLoader::_first_entry = NULL;
ClassPathEntry* ClassLoader::_last_entry = NULL;

 

          最后,最终的 class 加载动作是由 ClassLoader::load_classfile 方法完成的,位于 openjdk\hotspot\src\share\vm\classfile\classLoader.cpp 文件:

 

instanceKlassHandle ClassLoader::load_classfile(symbolHandle h_name, TRAPS) {
  VTuneClassLoadMarker clm;
  ResourceMark rm(THREAD);
  EventMark m("loading class " INTPTR_FORMAT, (address)h_name());
  ThreadProfilerMark tpm(ThreadProfilerMark::classLoaderRegion);

  stringStream st;
  // st.print() uses too much stack space while handling a StackOverflowError
  // st.print("%s.class", h_name->as_utf8());
  st.print_raw(h_name->as_utf8());
  st.print_raw(".class");
  char* name = st.as_string();

  // Lookup stream for parsing .class file
  ClassFileStream* stream = NULL;
  int classpath_index = 0;
  {
    PerfTraceTime vmtimer(perf_accumulated_time());
    ClassPathEntry* e = _first_entry;
    while (e != NULL) {
      stream = e->open_stream(name);
      if (stream != NULL) {
        break;
      }
      e = e->next();
      ++classpath_index;
    }
  }

  instanceKlassHandle h(THREAD, klassOop(NULL));
  if (stream != NULL) {

    // class file found, parse it
    ClassFileParser parser(stream);
    Handle class_loader;
    Handle protection_domain;
    symbolHandle parsed_name;
    instanceKlassHandle result = parser.parseClassFile(h_name,
                                                       class_loader,
                                                       protection_domain,
                                                       parsed_name,
                                                       CHECK_(h));

    // add to package table
    if (add_package(name, classpath_index, THREAD)) {
      h = result;
    }
  }

  return h;
}

 

          从上面的分析可以看出,若需要改变 JRE 默认 boot classpath 配置,加入自定的 jar 文件,则需要更改 openjdk\hotspot\src\share\vm\runtime\os.cpp 文件,并重新编译 jvm,而且如果 boot classpath 包含的默认 jar 文件过多、容量增大,还会影响JVM的加/下载、启动时间。

 

          希望通过本次对 JRE 无奈的 hack 过程所做的浅显研究,能够在后续有机会做更深入的学习、研究、收获。

 

作者:lzy.je
出处:http://lzy.iteye.com
本文版权归作者所有,只允许以摘要和完整全文两种形式转载,不允许对文字进行裁剪。未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。


// 2009.06.14 01:10 添加 ////

          推荐一篇比较详细介绍 bootstrap class loader 和 boot classpath 和相关内容的文章,篇幅不长值得快速一览。参见附件 BootClasspath.pdf

 

// 2009.08.18 16:04 添加 ////

 

          在编译 Hack/替换的代码时,一定要注意编译器(javac)版本代码版本兼容性生成类文件的虚拟机版本 3 件事。否则编译虽然能够成功,但在 Hack/替换后会出现“java.lang.UnsupportedClassVersionError”异常。在 IBM JDK 提供的 javac 编译器上,可以通过“-source <版本>”配置代码版本兼容性,通过“-target <版本>”生成指定虚拟机版本的类文件。

 

  • 大小: 23.5 KB
  • 大小: 25.7 KB
13
2
分享到:
评论
4 楼 lzy.je 2009-06-17  
whitesock 写道

你是否测试过如果Log4j的代码中如果调用了System.out.println或者System.err.println方法后会是什么后果? (log4j的LogLog类的部分方法里就有这些调用)


OK。递归的的确问题需要考虑,但可以通过在println方法中加判断解决。
3 楼 whitesock 2009-06-17  
你是否测试过如果Log4j的代码中如果调用了System.out.println或者System.err.println方法后会是什么后果? (log4j的LogLog类的部分方法里就有这些调用)
2 楼 lzy.je 2009-06-16  
mxswl 写道

不难理解,不过总觉得有点无聊


这是实际需要。至少讨论了一种可能的方式。
1 楼 mxswl 2009-06-16  
不难理解,不过总觉得有点无聊

相关推荐

    JRE瘦身 jre减肥 精简jre jre精简

    因为很多用户的电脑上没有合适版本的jre。用一个软件的同时必须安装另外一个软件(jre)会给人一种流氓软件强行捆绑的感觉,而很多用户对此很抵触。更不要提用户电脑上的jre版本千变万化(有没有jre,是微软的还是...

    jre-8u241-linux-i586.tar.gz

    JRE8.JRE8.JRE8.JRE8.JRE8.JRE8.JRE8.JRE8.JRE8.JRE8.JRE8.JRE8.JRE8.JRE8.JRE8.JRE8.JRE8.JRE8.JRE8.JRE8.JRE8.JRE8.JRE8.JRE8.JRE8.JRE8.JRE8.JRE8.JRE8.JRE8.JRE8.JRE8.JRE8.JRE8.JRE8.JRE8.JRE8.JRE8.JRE8.JRE8....

    jre1.8免压缩

    jre 压缩包

    jre1.8.0_161

    jre1.8压缩包免安装 jre1.8压缩包免安装 jre1.8压缩包免安装 jre1.8压缩包免安装

    jre-11.0.10.zip

    jdk11中提取的jre11。 JDK11 占用 279MB 磁盘空间;JRE11 占用 158MB 磁盘空间;打包压缩后的 JRE11 仅占用 40.2MB 的磁盘空间。如果服务器资源紧张,安装这个就可以了

    jre1.6-windows

    单纯的jre安装,不是jdk集成,面向用户机

    JRE 6 Java SE Runtime Environment 6u45

    JRE6 , Java SE 运行时环境 6u45 ,Linux/windows系统下 32位/64位 安装包,包含以下版本: Linux x86 jre-6u45-linux-i586-rpm.bin  Linux x86 jre-6u45-linux-i586.bin  Linux x64 jre-6u45-linux-x64-rpm.bin ...

    jre-8u301.zip

    (1)jre-8u301-linux-i586.rpm (2)jre-8u301-linux-i586.tar.gz (3)jre-8u301-linux-x64.rpm (4)jre-8u301-linux-x64.tar.gz (5)jre-8u301-macosx-x64.dmg (6)jre-8u301-macosx-x64.tar.gz (7)jre-8u...

    jre1.8.0_211.rar

    jre1.8.0_211

    精简jre步骤 迷你jre制作过程

    精简jre步骤 1. 拷贝一个完整版的jre文件夹到D盘 2. 删除jre目录下所有出bin和lib目录的所有文件或目录 3. 打开cmd窗口,设置path路径为空,转到D:\jre\bin目录,运行java –version。正常显示当前Java的版本。 4. ...

    jre1.6.0绿色版

    jre1.6.0绿色版 环境变量还需自己设置

    精简出最小_jre 减少jre大小

    精简出最小_jre收藏 ,介绍了如何减少jre大小的方法。

    jre-8-linux-arm

    java运行环境arm版本,...此Jre不同于从官网上下载的arm版本,在自定义或自剪裁的arm版linux系统中,官网上下载的jre是跑不起来的,此压缩包中包含softFp以及hardFp的各4个版本,一共八个版本的Jre,总有一款适合你!

    JRE1.8.0-391

    JRE1.8.0_391新版JRE1.8

    瘦身后的jre包

    这个是jre是经过我自己处理的一个缩小版jre,瘦身后大小约为3M; 目前我自己在window平台的xp和win7下测试时没什么问题,下载后我在压缩包里面写了一个简单的bat文件,jre测试.bat,下载后,如果能够正常运行这个bat...

    Oracle Server JRE 7 Update 55

    jre-7u55-windows-x64 sql 2014 jre-7u55-windows-x64 sql 2014

    自己动手精简你的jre到做小

    自己动手精简我们jre,再打包成exe这样java程序就可以在windows下很好的运行了,而且jre体积也很小不影响下载!感兴趣的可以下载后动手精简一下你的jre,我的精简到了2M以内,做成exe后根本看不出来是java做的。里面...

    JRE 7 Java SE Runtime Environment 7u80

    JRE 7, Java SE 运行时环境 7u80,Linux/windows/Mac OS系统下 32位/64位 安装包,包含以下版本: Linux x86 jre-7u80-linux-i586.rpm Linux x86 jre-7u80-linux-i586.tar.gz Linux x64 jre-7u80-linux-x64.rpm ...

    jre 1.8.45 下载

    jre 1.8.45 下载

Global site tag (gtag.js) - Google Analytics