Java 字节码 类文件结构

Java 文件编译后会生成 .class 二进制文件,这个文件以 8 字节为单元组织。在 Class 文件中定义了一些数据类型,u1,u2,u4,u8 分别代表 1 个字节、2 个字节、4 个字节、8 个字节的无符号数。

Class 文件像一张大表格,以一定的格式记录。

ClassFile
{
	 magic_number;
	 minor_version;
	 major_version;
	 constant_pool_count;
	 constant_pool[];
	 access_flags;
	 this_class;
	 super_class;
	 interfaces_count;
	 interfaces[];
	 fields_count;
	 fields[];
	 methods_count;
	 methods[];
	 attributes_count;
	 attributes[];
}

Magic Number

每个 Class 文件头 4 个字节称为 Magic Number,用于确认该文件是否能被虚拟机接受。

java class magic number

Class 文件的 Magic Number 是 0xCAFEBABE .

apt install ghex

Class 版本

接着 Magic Number 后面 4 个字节就是 Class 文件的版本号

  • 第 5,6 个字节是次版本号 Minor Version
  • 第 7,8 个字节是主版本号 Major Version

Java 版本号从 45 开始,JDK 1.1 后每个 JDK 大版本发布主版本加 1,高版本的 JDK 向下兼容以前的 Class 文件,但不能运行以后的版本。

看到上图里面,5,6 个字节 0x0000,主版本号 0x0033,就是十进制的 51,说明这个 Class 文件可以被 JDK 1.7 或者以上版本虚拟机执行。

常量值 constant_pool

在主版本号后面是常量池入口,所有的变量,方法都会在该常量池有一份引用。因为常量值数量不固定,所以常量值入口会放置 u2 类型数据,表示常量池容量计数 constant_pool_count ,这个容量计数是从 1 开始。

常量池主要存放两大类常量:

  • 字面值 Literial
  • 符号引用 Symbolic References , 包含

      - 类和接口的全限定名 Fully Qualified Name
      - 字段名和描述符 Descriptor
      - 方法的名称和描述符
    

常量池中每一项都是一个表

第一位是 u1 类型标志位,取值 1 到 12,缺少标志为 2 的数据类型,该标志表示当前常量属于哪种类型。

类型 标志 描述
CONSTANT_Utf8_info 1 UTF-8 编码字符串
CONSTANT_Integer_info 3 整型字面值
CONSTANT_Float_info 4 浮点
CONSTANT_Long_info 5 长整型
CONSTANT_Double_info 6 双精度浮点
CONSTANT_Class_info 7 类或者接口符号引用
CONSTANT_String_info 8 字符串类型
CONSTANT_Fieldref_info 9 字符的符号引用
CONSTANT_Methodref_info 10 类中方法的符号引用
CONSTANT_InterfaceMethodref_info 11 接口中方法的符号引用
CONSTANT_NameAndType_info 12 字段或方法的部分符号引用

如果看二进制的 Class 文件比较麻烦,JDK 中提供了了分析 Class 文件字节码的工具:javap

javap -verbose filename.class

可以看到类似如下的内容:

Constant pool:
   #1 = Methodref          #11.#36        // java/lang/Object."<init>":()V
   #2 = Fieldref           #37.#38        // java/lang/System.out:Ljava/io/PrintStream;
   #3 = String             #39            // hello
   #4 = Methodref          #40.#41        // java/io/PrintStream.println:(Ljava/lang/String;)V

访问标志 access flags

常量池结束后接 2 字节代表访问标志 access_flag. 用于识别类或接口层次的访问信息,包括 Class 是类还是接口,是否定义为 public,是否定义为 abstract 类型,类是否被声明为 final .

类索引 this_class 父索引 super_class 接口索引 interfaces

Class 文件由这个三个数据来确定类的继承关系。

  • 类索引确定类的全限定名
  • 父类索引确定这个父类的全限定名,除 java.lang.Object 外,所有 Java 类的父类索引都不是 0
  • 接口索引描述类实现了哪些接口,按照实现的顺序排列

类索引、父类索引和接口索引都按照顺序排列在访问标志后,类索引和父类索引用两个 u2 类型索引值表示,各自指向 CONSTANT_Class_info 的类描述符常量,通过 CONSTANT_Class_info 类型的常量中的索引值来找到定义在 CONSTANT_Utf8_info 类型的常量中的全限定名字符串。

接口索引,入口是 u2 类型数据的接口计数 interfaces_count ,表示索引表容量,如果没有实现任何接口,则计数器为 0。

字段表集合 field info

用于描述接口或者类中声明的变量,包括类级变量或者实例级变量,但不包括方法内部声明变量。

  • 字段作用域 public private protected
  • 类级变量还是实例变量 static
  • 可变性 final
  • 并发可见性 volatile,是否强制从主存读写
  • 可否序列化 transient
  • 字段基本类型,对象,数组
  • 字段名称

方法表集合

方法表结构和字段表相同,依次包括了访问标志 access flags, 名称索引 name index, 描述符索引 descriptor index , 属性表集合 attributes

属性表集合 attribute info

与 Class 文件中其他数据项目要求严格的顺序、长度和内容不同,属性表集合限制宽松,不要求属性表具有严格顺序,只要不与已有的属性名重复,任何人实现的编译器都可以向属性中写入自己定义的属性信息,Java 虚拟机在运行时会忽略掉它不认识的属性。


2014-04-05 java , class , cross-platform , bytecode

CPU知识

###步进 所谓步进:同一处理器型号随着时间推移,会有一些小的改进,厂商把这些小的改进通过新的步进来体现。 (通过汉语意思理解:就是与之前相比进了一小步,针对同一型号) 酷睿 i3/i5全线将从C2步进转换到K0,预计在品质等诸多方面比上一个步进要更加出色。 现在知道了吧,不用去关注它,只要知道是处理器在架构或者其他方面提高了就行。 步进更多的知识详见百度百科,里面讲到了用CPU-z识别当前使用的CPU的步进信息,如何通过编码知道步进信息。

###CPU各版本信息 ####ES 测试版CPU,发给制造商用于各类测试的版本,因为不是免费提供,intel无法回收CPU,测试结束以后部分CPU会流入市场,该类CPU如要购买最好先搜一下看看ES版是否存在一些BUG

####QS 最后一个版本的ES,一般用工具检测,固件烧录的也是ES,用于区分正式版

####BGA、PGA

BGA和PGA是intel的两种封装类型,BGA没有阵脚,直接焊在主板上,PGA带有阵脚,可以插在socket插座上使用,一些JS把BGA的CPU拆掉私自焊针脚来销售,稳定性极差,价格要低很多,温度要高一些,BGA和PGA无法用软件区分,只能拆开自己检查

引用:低电压板,L,SP,SU开头的型号,基本都是bga的 引用:http://www.xj123.info/1717.html

###CPU后面跟随的数字 I3 后面有四个数字 拿I3 3220为例, 第一个3就是第三代,所以2100就是第二代,530(三个数字)就是第一代 第二个2就是规格,一般取比第一个数字小1的数字,比如第一个是4,后面就是3,如果小于3,那就是进一步降低主频或删减缓存的缩水货。 第三个2就是等级,档次,越高越好。 最后一个0只是加上去为了英语发音好读。

I5也是一样,比如I5 3570K 第一个数字3代表第三代,第二个数字固定为5,小于5的就是精简过规格的产品,第三个是等级。

另外关于第二个数字还有一个小秘密,一般3以下都是I3(也有双核I5,很少),3-5是I5,6-9是I7。 引用:http://zhidao.baidu.com/question/573950249.html


2014-03-13

Android SDK环境变量配置

Android SDK就是Google提供的Android开发工具包,之前请先配置好JDK

###Android SDK下载地址

  • Google搜索Android SDK
  • 或者https://developer.android.com/sdk/index.html

###配置Android SDK环境变量

  1. 下载Android SDK,点击安装,直接默认路径即可! 下载地址:https://developer.android.com/sdk/index.html

  2. 默认路径安装后,安装完成,开始配置环境变量。

  3. 打开计算机属性——高级系统设置——环境变量(如上文)

  4. 新建一个环境变量,变量名:ANDROID_HOME,变量值:D:\Android\android-sdk(以你安装目录为准,确认里面有tools和add-ons等多个文件夹),点击确认。

  5. 在用户变量PATH后面加上变量值;%ANDROID_HOME%\platform-tools;%ANDROID_HOME%\tools;点击确认即可

  6. Android SDK配置完成,接下来验证配置是否成功。

  7. 点击运行——输入cmd——回车——输入adb——回车,如果出现一堆英文,即表示配置成功,输入Android,启动Android SDK Manager。

###目录tools和platform-tools的区别

Android sdk目录里,有一些文件夹:

  • tools:该目录存放大量的Android开发,调试工具,该目录下存放大量Android开发工具,例如SDK Manager、androidavd、emulator、ddms等等。
  • platforms-tools:该文件夹存放Android平台和相关工具,存放Android不同平台的相关工具;随着SDK更新版本,这里的工具会有相应更新变化,但是一般都是向后兼容。最常用的是Android Debug Bridge(adb)工具
  • add-ons:该目录存放额外的附件软件。刚解压时该目录为空。
  • platforms:该目录存放不同版本的Android版本。刚解压时该目录为空。
  • SDK Manager.exe:该程序就是Andriod SDK管理器。
  • AVD Manager.exe:该程序就是Andoid虚拟设备。
  • docs:该文件夹存放了Android SDK开发文件和API文档等
  • samples:该文件夹存放不同的Android平台和示例程序。

参考:http://blog.csdn.net/rflyee/article/details/8973529


2014-03-04 Android , JDK

Vim 插件介绍:Nerdtree

地址

介绍

:help NERDTree

Config

通常情况下会设置一个 map

nmap <F2> :NERDTreeToggle<cr>

然后设置

" nerd comment
" Add spaces after comment delimiters by default
let g:NERDSpaceDelims = 1

" Use compact syntax for prettified multi-line comments
let g:NERDCompactSexyComs = 1

" Align line-wise comment delimiters flush left instead of following code indentation
let g:NERDDefaultAlign = 'left'

" Set a language to use its alternate delimiters by default
let g:NERDAltDelims_java = 1

" Add your own custom formats or override the defaults
let g:NERDCustomDelimiters = { 'c': { 'left': '/**','right': '*/' } }

" Allow commenting and inverting empty lines (useful when commenting a region)
let g:NERDCommentEmptyLines = 1

" Enable trimming of trailing whitespace when uncommenting
let g:NERDTrimTrailingWhitespace = 1

" Highlight Cursor line
let g:NERDTreeHighlightCursorline = 1
" Show hidden files
let NERDTreeShowHidden=1
" NERDTree will recursively open dirs with only one child
let NERDTreeCascadeOpenSingleChildDir=1

Keyboard Mappings

使用 :help NERDTreeMappings 来查看插件所有的绑定,这一些是日常中使用频率比较高的:

  • Enter 在窗口打开文件
  • i 水平分割线分割,如果有一个打开的窗口,那么会分成上下两个窗口
  • s 垂直分割线分割,左右两个窗口
  • I 显示或者隐藏 hidden file
  • m 菜单,m 是一个非常强大的命令,可以支持新增,删除,重命名,复制等等操作
  • r 递归刷新当前目录
  • R 递归刷新 root 目录
  • ?

2014-02-21 vim , nerdtree , vim-tips , vim-plugin

Blogger 主题中Widget使用

Blogger 的主题和插件有自带的一套语法,下面就是 Blogger 主题中关于 Widget 的使用说明。

Widget Tags for Layouts

标签是用来创建最基本的widget的,描述在 [Page Element Tags for Layouts](http://help.blogger.com/bin/answer.py?answer=46888) 中.而这篇是描述在widget标签中具体可以设置哪些东西的,例如你可以在widget中放置什么. 第一件事就是放置一个闭合的标签,像这样: `<b:widget [...attributes...] />` 展开之后是这样的: `<b:widget [...attributes...]>

`

接下来谈论一下可以在widget中放置什么

Includes

Widget的内容包含在”includable” section中,像这样:

<b:includable id='main' var='thiswidget'>
    [insert whatever content you want here]
</b:includable>

includable的属性有:

  • id:(必须)唯一标识符,由字母和数字组成
  • var:(可选)标识符,由字母数字组成,用来引用section中的data.(具体请见下面说的data section)

每一个widget必须有一个具有id='main'的 includable 。通常这个widget的大部分内容都包含在这个includable中.

如果你有很多具有不同id属性的includable,他们不会自动的显示出来.但是如果你有一个includable带有id='new'的属性,那么在main includable中通过<b:include name='new' />就能够引用,并显示出来.

b:include标签的属性如下:

  • name:(必须)标识符由字母和和数字组成,它必须是在同一个widget中已经存在的b:includable的id
  • data:(可选)An expression or peice of data to pass on to the includable section. This will become the value of the var attribute in the includable.

简单例子同来讲解b:includableb:include的用法.Loops和data会在后面讲述,这里主要要理解’main’ section是如何包含 ‘post’ section的.main包含了一个includable,传递了一个data叫做’i’,included section引用做’p’,使用p访问title.

<b:includable id='main'>
   <b:loop var='i' values='posts'>
      <b:include name='post' data='i'/>
   </b:loop>
</b:includable>

<b:includable id='post' var='p'>
   Title: <data:p.title/>
</b:includable>

下面这段的意思就是说如果你想重复使用代码,可以将代码放到includable中,然后用include引用.就不翻译了. Includes are most useful if you have a section of code that you want to repeat multiple times in different places. You can just write the code once, put it inside a b:includable, then use b:include wherever you want it to appear. If you don’t need to do that, then you can just stick with the single main includable and not worry about the rest. (Note that the main includable is included automically – is unnecessary.)

Data

data:标签大概是最为重要的一个标签了,因为它是获取一切事实数据的途径,一些例子如下:

<data:title/>

or

<data:photo.url/>

这个例子非常简单,他能够在几乎所有的widget中起作用,因为大多数widget有title.上面的例子就是打印出widget的title.第二个例子是一个较为复杂的变量,获取photo的url.

有很多data你能够通过data:标签来获取,查阅 comprehensive list 查询需要的data,一些data只能在特定的widget中使用.

Loops

b:loop 标签让你重复使用section中内容.通常用在打印给定的一系列post,或者每一条留言,或者每一个label,等等.通常用法,像这样:

<b:loop var='identifier' values='set-of-data'>
   [repeated content goes here]
</b:loop>
  • identifier 可以选用任何名字,用来代表list中每一个item.可以简单的设定为”i”.
  • set-of-data 可以是在 data tags article中定义的任何一系列data

例如,在blog post widget中, posts是一个list,下面的代码是遍历每一个post,打印出每一个的标题title

<b:loop var='i' values='data:posts'>
   <h2><data:i.title/></h2>
</b:loop>

注意,”i”是每一个post,在循环中使用i获取每一个post的title

if / Else

if/else像很多编程语言一样,条件判断,这里是选择哪些显示和哪些不显示.通常如下:

<b:if cond='condition'> 
   [content to display if condition is true]
<b:else/>
   [content to display if condition is false]
</b:if>

b:else tag是可省略的. 而</b:if>是不可省略的. 如果没有else子句,那么只有满足if条件才会执行if子句中代码,不然什么都不执行

条件语句中你可以放置任何结果是true或者false的语句,有一些data本身就表示true/false,像post中allowComments.其他data,你可以用来比较得到结果.一些例子:

<b:if cond='data:post.showBacklinks'> True if the current post is set to show backlinks.
<b:if cond='data:blog.pageType == "item"'> True if the current page is an item page (post page).
<b:if cond='data:displayname != "Fred"'> True if this is not Fred's display name.
<b:if cond='data:post.numComments > 1'> True if the current post has more than one comment.

翻译自:Blogger Help Widget Tags for Layouts


2014-02-04 Google , Blogger , Knowledge

HAProxy 转发 shadowsocks

HAProxy is free, open source software that provides a high availability load balancer and proxy server for TCP and HTTP-based applications that spreads requests across multiple servers. It is written in C and has a reputation for being fast and efficient.

简单的来说 HAProxy 就是一个负载均衡,TCP 和 HTTP 的代理程序,开源,高可用,C 写成。他原本的作用是将前端的大量流量分发到后端的服务器中,用于负载特别大的 WEB 网站的,这里被大材小用了。

使用 HAProxy 中转 SS 流量至少需要两台 VPS,国内一台,国外一台,SS 客户端直接连国内的 VPS,而通过国内的 VPS 转发流量到国外的 VPS 上。

安装

sudo apt install -y haproxy

配置

HAProxy 的配置文件在 /etc/haproxy/haproxy.cfg 下:

global
    ulimit-n  51200

defaults
    log global
    mode    tcp
    option  dontlognull
        timeout connect 5000
        timeout client  50000
        timeout server  50000

frontend ss-in
    bind *:8888
    default_backend ss-out

backend ss-out
	balance roundrobin
    server server1 [VPS-IP]:8888 maxconn 20480
    server server2 [VPS2-IP]:8888 maxconn 20480

主要的配置就是 frontendbackend,也很好理解,将入站的 8888 端口中的流量转发到 VPS 的 8888 端口。然后重启 HAProxy 即可

sudo /etc/init.d/haproxy restart

启动之后,本地的 SS 客户端直接连国内的 IP 即可,需要注意的是配置用的是 ssserver 的配置。

监控页面

配置监控界面监控转发流量:

listen stats    #定义监控页面 $
	bind *:1080                     #绑定端口 1080$
	mode http                       # http mode$
	stats refresh 30s               #每 30 秒更新监控数据 $
	stats uri /stats                #访问监控页面的 uri$
	stats realm HAProxy\ Stats      #监控页面的认证提示 $
	stats auth username:password    #监控页面的用户名和密码 $

reference


2014-02-03 haproxy , shadowsocks , ss , tcp , load-balancer , proxy-server , proxy

Linux Mint 下开机自启动应用

在 Cinnamon 桌面环境下有一个开机启动应用的管理程序叫做 startup applications,用该程序来管理开机启动应用是非常方便的。更准确的来说应该是 login 到桌面环境中启动的应用程序管理。

使用

命令行是

cinnamon-settings startup

同样在 gnome 的桌面环境下也有一个相似的 gnome-session-properties 但是这个应用没有 Cinnamon 提供的方便友好,一个很大的差别就是 Cinnamon 提供的 startup 工具允许用户直接选择已经安装的应用,而 gnome 提供的只允许用户自己选择执行脚本(命令),自己选择图标,标题等等。Cinnamon 帮我们做了很多友好的工作。

实际上所有开机自启动的配置都在 ~/.config/autostart 目录中,每一个启动项都以自己名字开头,叫做 goldendict.desktop 这样的名字,包含如下内容。

[Desktop Entry]
Type=Application
Terminal=false
Categories=Office;Dictionary;Education;Qt
Name=GoldenDict
GenericName=Multiformat Dictionary
Comment=GoldenDict
Encoding=UTF-8
Icon=goldendict
Exec=goldendict
X-GNOME-Autostart-enabled=true
NoDisplay=false
Hidden=false
Name[en_US]=GoldenDict
Comment[en_US]=GoldenDict
X-GNOME-Autostart-Delay=100

其中定义了常见的配置,包括启动命令,图标,名字等等。

reference


2014-02-02 linux , linux-mint , cinnamon , startup , application

manual guide tutorial 区别

manual 一般翻译为手册,指南。通常是制造商提供的一份详细的操作指示,更具体来说一般为官方提供的。

guide 向导,指南。是一个比较宽泛的词,鉴于 manual 可能是一份文档,游戏的小册子,或者更加复杂的汽车的手册,但是 guide 可能就是一个卡片,或者介绍的视频。guide 可能是一个比较细节的说明,但也可能是一个简单的介绍或者说明。

tutorial 通常翻译为教程,通常是针对无经验的初学者提供 step-by-step 每一步详细的说明。

cookbook 直译是食谱,在 鉴别 Mastering Cookbook xxx in Action 系列图书 文章中就已经解释了,通常指的是比较实用的一些用例。

reference 一般译作参考,引用,这个 reference 倒是不会产生什么歧义。我的文章中也经常使用。

reference


2014-02-01 document , guide , difference

Java 查漏补缺之:赋值语句的返回值

在看 JDK 源码 HashMap 时发现在 put 方法实现过程中使用了下面的语句

if ((tab = table) == null || (n = tab.length) == 0)
    n = (tab = resize()).length;

能够看到在判断语句中使用了赋值语句的结果来和 null 和 0 比较,再比如读写文件时会有

while ((line = reader.readLine()) != null) {
    out.append(line);
}

就能发现 Java 中的赋值语句是有返回值的,并且也不是简单的 boolean 。事实上赋值语句返回的是左侧变量的引用值,也就是右侧的结果

@Test
public void testAssignment() {
    int i;
    System.out.println(i=0);
    System.out.println(i=1);
}

输出分别是 0 和 1.


2014-01-20 java , jdk , programming

学车记

学车到现在已经接近尾声,拖了两年之久的事情终于要有点眉目了。现在只剩下最后的理论—-科目四的考试了,这个考试应该也和科目一的理论差不多,只是最后多了五道多选题而已。现在回想起来,暑假,寒假,寒假,多少时间花在这个上面。断断续续不知道是福还是祸,考试从人监考一直到现在红外线考,变化多多,训练的方法也变化多多。

之前一个寒假我记得当时考试,倒桩和小路考是分开考的,而今年寒假回来就变成一起考了。以前小路考还考单边桥,而现在不见了。以前小路考是一次性,如果通过就通过,现在是有两次机会,第一次不过还有第二次机会。总之里面的变化任谁也搞不清楚。反正这些也不管了,都是过去的事情了。

下面就做一些记录,这是我一边学车,一边记录下来的:

##小路考 上车,安全带(安全带不能带反,因为车上有感应装置,如果安装反了感应不到就是不及格) 起步(不能停顿超过30秒),左方向灯 侧方停车位,开车到看到左边后视镜看见库一角,一圈方向,倒档,车身与库对角线齐平,返回一圈方向,看右后轮胎,快进库时,反方向打死方向盘,进库,出来时方向灯 s路,直着进弯道,看黄线到车头三分之一处一圈方向,车走正了,返回一圈方向,然后 坡上定点停车与坡道起步,车尽量往右边靠30cm,车头要在停止范围内,起步方向灯 直角转弯,反光镜到黄线齐平,打死方向 倒车入库,到黄线打死方向,看左反光镜,车声0.5米,返回半圈方向,看车身与黄线平行,将方向放正

##大路考

上车,安全带,座椅,踩离合,空档发动,大车灯,方向灯切换一下(后来分完车,车上教练教了一个好办法:上车1.空档 2.发动 3.大灯 4.方向 下车1.方向 2.大灯 3.熄火 4.挂一档,正好完全相反) 报告考官,灯光仪器正常,请求起步 方向灯,远近大灯,左右反光镜,挂一档,松离合,松手刹。 挂二档 靠边停车,转向灯,减速 下车,右转向,空档,手刹,关大灯,关方向,熄火,挂一档,松安全带,看反光镜,后面车,下车

总之上面就是这些天反复练习的东西,就这么多。考试也就这么多。


2014-01-13 学车 , beginner

电子书

最近文章

  • 配置 Rime 在 Vim 下退出编辑模式时自动切换成英文输入法 半年以前在 Obisidian 的文章下面有人曾经问过我一个问题,如何在 Vim 或者其他使用 Vim 模式的编辑器,比如 IntelliJ,或者 Obisidian 开启 Vim 模式后方便地切换中英文输入法,因为在编辑模式和普通模式下,需要经常切换输入法,使得体验变得非常槽糕。
  • Asus RT-AC86U 设置 前些天给家里买手机正好凑单了一个 Asus RT-AC86U,正好可以代替出了两次故障的小米 3G。
  • 扩展 Proxmox 系统分区以及 Proxmox 文件系统初识 昨天想要扩展一下之前安装的 Proxmox 容量,对系统进行了一次关机,然而关机之后就悲剧的发现在 U 盘中的系统启动不了了,将 U 盘拔下检测之后发现 U 盘可能挂了,一个全新的 U 盘,在连续 192 天运行之后挂掉了。无奈之下只能想办法先恢复一下 Proxmox 系统以及安装在系统之上的 OpenMediaVault 了。
  • 『译』我最喜欢的命令行工具 偶然间看到一篇介绍 cli 的文章,感觉写得不错,正好借此机会也整理一下我之前使用过,以及觉得非常值得推荐的 CLI 工具。
  • 使用 Clonezilla 将硬盘中系统恢复到虚拟机中 今年陆陆续续将工作的环境迁移到了 macOS,虽然已经把日常的资料迁移到了 macOS,但是之前的 Linux 上还有一些配置,以及可以的一些测试还需要用到 Linux 虚拟机,所以我就想能不能用 Clonezilla 将磁盘中的系统备份然后恢复到虚拟机里面。因为我发现 macOS 下的 Fusion 还是很强大的。