文件流

文件流

在前一篇介绍ClassFileParser类时简单提了一下_stream属性,这个属性保存的是字节码文件流。如果要读取Class文件的内容,首先需要获取文件对应的字节流,ClassFileStream 内部维护了一个buffer,该buffer指向Class文件所对应的字节流。

ClassFileStream对象是在ClassLoader::load_classfile()函数中创建的,这个方法在之前介绍类的双亲委派机制时提到过,当装载一个类时,可能会调用到SystemDictionary::load_instance_class()函数,而这个函数会体现出“双亲委派”的逻辑。如果使用启动类加载器,那么可能需要调用load_classfile()方法装载类。load_classfile()方法的实现如下:

源代码位置:src/share/vm/classfile/classLoader.cpp
instanceKlassHandle ClassLoader::load_classfile(Symbol* h_name, TRAPS) {

  stringStream st;
  st.print_raw(h_name->as_utf8());
  st.print_raw(".class");
  const char* name = st.as_string(); // 通过st获取对应的文件名

  // Lookup stream for parsing .class file
  ClassFileStream* stream = NULL;
  {
    ClassPathEntry* e = _first_entry;
    while (e != NULL) {
      stream = e->open_stream(name, CHECK_NULL);
      if (stream != NULL) {
        break;
      }
      e = e->next();
    }
  }
  ...
}

遍历class_path找到要加载的类文件,获取到文件的绝对路径后就创建ClassFileStream对象。ClassPathEntry 是一个链表结构(因为class path有多个),同时在ClassPathEntry中还声明了一个虚函数open_stream()。这样就可以通过循环遍历链表上的结构,直到查找到某个路径下名称为name的文件为止,这时候open_stream()函数会返回ClassFileStream实例。

在load_classfile()方法中获取到ClassFileStream实例后会调用ClassFileParser类中的parseClassFile()方法,如下:

instanceKlassHandle ClassLoader::load_classfile(Symbol* h_name, TRAPS) {
  // ...

  instanceKlassHandle h;
  if (stream != NULL) {
    // class file found, parse it
    ClassFileParser parser(stream);
    ClassLoaderData* loader_data = ClassLoaderData::the_null_class_loader_data();
    Handle protection_domain;
    TempNewSymbol parsed_name = NULL;
    instanceKlassHandle result = parser.parseClassFile(h_name,loader_data,protection_domain,parsed_name,false,CHECK_(h));
    // add to package table
    if (add_package(name, classpath_index, THREAD)) {
      h = result;
    }
  }

  return h;
}

调用parseClassFile()方法后返回表示Java类的instanceKlass对象,最终方法返回的是操作instanceKlass对象的句柄instanceKlassHandle。下一篇开始将详细介绍parseClassFile()方法的实现。

简单介绍一下ClassFileStream类中的一些被频繁调用的方法,如下:

u1 ClassFileStream::get_u1(TRAPS) {
  return *_current++;
}

u2 ClassFileStream::get_u2(TRAPS) {
  u1* tmp = _current;
  _current += 2;
  return Bytes::get_Java_u2(tmp);
}

u4 ClassFileStream::get_u4(TRAPS) {
  u1* tmp = _current;
  _current += 4;
  return Bytes::get_Java_u4(tmp);
}

u8 ClassFileStream::get_u8(TRAPS) {
  u1* tmp = _current;
  _current += 8;
  return Bytes::get_Java_u8(tmp);
}

void ClassFileStream::skip_u1(int length, TRAPS) {
  _current += length;
}

void ClassFileStream::skip_u2(int length, TRAPS) {
  _current += length * 2;
}

void ClassFileStream::skip_u4(int length, TRAPS) {
  _current += length * 4;
}

Class文件由字节为单位的字节流组成,所有的16位、32位和64位长度的数据将被构造成 2个、4个和8个8字节单位来表示。多字节数据项总是按照Big-Endian的顺序进行存储,而x86等处理器则是使用了相反的Little-Endian顺序来存储数据。
因此,在x86平台上需要进行转换。代码如下:

源代码位置:openjdk/hotspot/src/cpu/x86/vm/bytes_x86.hpp
// Efficient reading and writing of unaligned unsigned data in Java
// byte ordering (i.e. big-endian ordering). Byte-order reversal is
// needed since x86 CPUs use little-endian format.
static inline u2   get_Java_u2(address p)           { return swap_u2(get_native_u2(p)); }
static inline u4   get_Java_u4(address p)           { return swap_u4(get_native_u4(p)); }
static inline u8   get_Java_u8(address p)           { return swap_u8(get_native_u8(p)); }

调用的相关函数如下:  

源代码位置:openjdk/hotspot/src/cpu/x86/vm/bytes_x86.hpp
// Efficient reading and writing of unaligned unsigned data in platform-specific byte ordering
// (no special code is needed since x86 CPUs can access unaligned data)
static inline u2   get_native_u2(address p)         { return *(u2*)p; }
static inline u4   get_native_u4(address p)         { return *(u4*)p; }
static inline u8   get_native_u8(address p)         { return *(u8*)p; }

调用的swap_u<x>系列的函数实现如下  

源代码位置:openjdk/hotspot/src/os_cpu/linux_x86/vm/bytes_linux_x86.inline.hpp
inline u2   Bytes::swap_u2(u2 x) {
  return bswap_16(x);
}
inline u4   Bytes::swap_u4(u4 x) {
  return bswap_32(x);
}
inline u8 Bytes::swap_u8(u8 x) {	
  return bswap_64(x);
}

如上是针对基于Linux内核的ubuntu的x86架构下64位版本代码的实现。其中调用的bswap_<x>系列函数是gcc提供的几个内建函数。
由于HotSpot需要跨平台兼容,所以会增加一些针对各平台的特定实现,如Bytes::swap_u2()函数的完整实现如下:

inline u2   Bytes::swap_u2(u2 x) {
#ifdef AMD64
  return bswap_16(x);
#else
  u2 ret;
  __asm__ __volatile__ (
    "movw %0, %%ax;"
    "xchg %%al, %%ah;"
    "movw %%ax, %0"
    :"=r" (ret)      // output : register 0 => ret
    :"0"  (x)        // input  : x => register 0
    :"ax", "0"       // clobbered registers
  );
  return ret;
#endif // AMD64
}

其中的AMD64表示x86架构下的64位指令集,所以笔者当前的机器会选择AMD64位下的实现。如果是非AMD64位的系统,使用gcc内联汇编来实现相关的功能,其将x 的值读入某个寄存器,然后在指令中使用相应寄存器,并将该值移动到%ax中,然后通过xchg 交换%eax中的高低位。然后将最终的结果送入某个寄存器,最后将该结果送到ret中。 

相关文章的链接如下:

1、在Ubuntu 16.04上编译OpenJDK8的源代码 

2、调试HotSpot源代码

3、HotSpot项目结构 

4、HotSpot的启动过程 

5、HotSpot二分模型(1)

6、HotSpot的类模型(2)  

7、HotSpot的类模型(3) 

8、HotSpot的类模型(4)

9、HotSpot的对象模型(5)  

10、HotSpot的对象模型(6) 

11、操作句柄Handle(7)

12、句柄Handle的释放(8)

13、类加载器 

14、类的双亲委派机制 

15、核心类的预装载

16、Java主类的装载  

17、触发类的装载  

18、类文件解析 

作者持续维护的个人博客classloading.com。

关注公众号,有HotSpot源码剖析系列文章!

<span>文件流</span> 

  

  

 

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/2668.html原文链接:https://javaforall.net

赞 (0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • 命令解压war包_tomcat war包不解压

    命令解压war包_tomcat war包不解压把java程序打成war包。起先用zip命令打包,起先可以用,后来却无法使用。今天找到一个更好的办法。用jar命令,前提是要安装jdk。操作:1、把当前目录下的所有文件打包成game.warjar-cvfM0game.war./-c创建war包-v显示过程信息-f-M-0这个是阿拉伯数字,只打包不压缩的意思解压game.warjar-xvfgame.war解压到当前目录…

    2022年10月24日
  • 服务器托管双线技术方案怎么写_自己搭建内网穿透服务器全端口

    服务器托管双线技术方案怎么写_自己搭建内网穿透服务器全端口多线路接入技术就是在互联网数据中心(IDC)通过特殊的技术手段把不同的网络接入商(ISP)服务接入到一台服务器上或服务器集群,使服务器所提供的网络服务访问用户能尽可能以同一个ISP或互访速度较快的ISP连接来进行访问,从而解决或者减轻跨ISP用户访问网站的缓慢延迟(南北网络瓶颈)问题。多线路接入是一个技术概念可以有多种具体实现方式,由于大多用户都是网通与电信,为了见简单起见,我们只讨…

    2022年10月23日
  • 黑盒 测试用例设计方法「建议收藏」

    黑盒 测试用例设计方法「建议收藏」概述黑盒测试用例设计方法包括等价类划分法、边界值分析法、错误推测法、因果图法、判定表驱动法、正交试验设计法、功能图法等。等价类划分法概念等价类划分法是把程序的输入域划分成若干部分(子集),然后从每个部分中选取少数代表性数据作为测试用例。每一类的代表性数据在测试中的作用等价于这一类中的其他值。等价类划分法的应用1.等价类是指某个输入域的子集合。在该子集合中,各个输入数据对于揭露程序中的错…

    2022年6月11日
  • pycharm上传代码到gitlab_pycharm配置git

    pycharm上传代码到gitlab_pycharm配置git让git忽略掉一些文件(.gitignore)#pycharm.idea/.DS_Store__pycache__/*.py[cod]*$py.class#Djangostuff:local_settings.py*.sqlite3#databasemigrations*/migrations/*.py!*/migrations/__init__.pygit管理项目

    2022年8月28日
  • 游戏建模,室内设计哪个更有前景?[通俗易懂]

    游戏建模,室内设计哪个更有前景?[通俗易懂]游戏建模职业分类及发展:进入游戏建模行业你可以选择不同的发展方向,比如:(1)手绘3D美术设计师:制作纯手绘风格游戏的所有3D物品如:角色、道具、建筑、山体;(2)次世代3D美术设计师:制作写实次世代风格游戏的所有3D物品,如:角色、道具、建筑。(3)关卡设计师:根据游戏风格要求,使用模型资源,搭建3D游戏世界(4)模型师:制作3D打印、影视动画中的所有模型。如:角色、道具、建筑、山体。次世代美术设计师做什么?次世代游戏:“次世代游戏”指代和同类游戏相比下更加先进的游戏,即“下一代游戏”。

    2022年5月12日
  • 在微型计算机中1gb等于多少字节,1GB等于多少字节「建议收藏」

    在微型计算机中1gb等于多少字节,1GB等于多少字节「建议收藏」1GB=1073741824字节。1KB=1024字节1M=1024KB1G=1024MB一小组相邻的二进制数码称为字节。通常是8位作为一个字节。它是构成信息的一个小单位,并作为一个整体来参加操作,比字小,是构成字的单位。扩展资料:数据的存储是以“字节”(Byte)为单位,数据传输大多是以“位”(bit,又名“比特”)为单位,一个位就代表一个0或1(即二进制),每8个位(bit,简写为b)组成一个…

    2022年5月9日

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号