2018年度最受欢迎软件——中国开源软件TOP20

RuoYi——带代码自动生成的基于SpringBoot的管理平台

https://gitee.com/y_project/RuoYi

内置功能

用户管理:用户是系统操作者,该功能主要完成系统用户配置。
部门管理:配置系统组织机构(公司、部门、小组),树结构展现支持数据权限。
岗位管理:配置系统用户所属担任职务。
菜单管理:配置系统菜单,操作权限,按钮权限标识等。
角色管理:角色菜单权限分配、设置角色按机构进行数据范围权限划分。
字典管理:对系统中经常使用的一些较为固定的数据进行维护。
参数管理:对系统动态配置常用参数。
通知公告:系统通知公告信息发布维护。
操作日志:系统正常操作日志记录和查询;系统异常信息日志记录和查询。
登录日志:系统登录日志记录查询包含登录异常。
在线用户:当前系统中活跃用户状态监控。
定时任务:在线(添加、修改、删除)任务调度包含执行结果日志。
代码生成:前后端代码的生成(java、html、xml、sql)支持CRUD下载 。
系统接口:根据业务代码自动生成相关的api接口文档。
在线构建器:拖动表单元素生成相应的HTML代码。
连接池监视:监视当期系统数据库连接池状态,可进行分析SQL找出系统性能瓶颈。

Druid——阿里巴巴开源的带监控功能的JDBC数据库连接池

Druid github地址

fastjson——阿里巴巴开源的JSON 转Java对象的包

AntDesign——AlipayUI 是蚂蚁金服针对 mobile 研发推出的一套基于 React 实现的 H5 组件库

里面有较多的DEMO,同时有较多的代码。
提供行业模板

同时提供axure和sketch下的网页设计模板,提升设计速度。

Hutools——Hutool是一个Java工具包,也只是一个工具包,它帮助我们简化每一行代码,减少每一个方法,让Java语言也可以“甜甜的”。

hutools

Guns

Guns基于SpringBoot 2,致力于做更简洁的后台管理系统,完美整合springmvc + shiro + mybatis-plus + beetl!Guns项目代码简洁,注释丰富,上手容易,同时Guns包含许多基础模块(用户管理,角色管理,部门管理,字典管理等10个模块),可以直接作为一个后台管理系统的脚手架!

xxl-job

XXL-JOB是一个轻量级分布式任务调度平台,其核心设计目标是开发迅速、学习简单、轻量级、易扩展。现已开放源代码并接入多家公司线上产品线,开箱即用。

apollo

Apollo(阿波罗)是携程框架部门研发的分布式配置中心,能够集中化管理应用不同环境、不同集群的配置,配置修改后能够实时推送到应用端,并且具备规范的权限、流程治理等特性,适用于微服务配置管理场景。

2018/12/15 posted in  java

gradle

2017/8/9 posted in  java

Hybris 经验总结——Debug模式

Hybris Debug 方法

Hybris Debug配置文件

对于Hybris的启动实际上是调用 _hybris-hankport_hybris_bin_platform 这个目录下面的hybrisserver.sh文件来启动执行的,vi这个执行文件可以看到

    case $MODE in
        "minimal" | "-m" )
            export WRAPPER_CONF="$CATALINA_BASE/conf/wrapper-minimal.conf"
            COMMAND="./catalina.sh run"
            ;;
        "debug" | "-d" )
            export WRAPPER_CONF="$CATALINA_BASE/conf/wrapper-debug.conf"
            COMMAND="./catalina.sh run"
            ;;
        "jprofiler" | "-j" )
            export WRAPPER_CONF="$CATALINA_BASE/conf/wrapper-jprofiler.conf"
            COMMAND="./catalina.sh run"
            ;;
        "version" | "-v" )
            COMMAND="java -cp ../lib/catalina.jar org.apache.catalina.util.ServerInfo"
            ;;
        * )
            export WRAPPER_CONF="$CATALINA_BASE/conf/wrapper.conf"
            COMMAND="./catalina.sh ${MODE}"
            ;;
    esac

所以对于Hybris服务来说,有几种启动方式,比如minimal模式、debug模式、jprofiler模式和默认模式。
可以通过下面方法启动

    sh hybrisserver.sh 
    sh hybrisserver.sh debug
    sh hybrisserver.sh jprofiler

可以看到启动不同的模式的时候实际就是加载了不同的wrapper容器而已。

wrapper-debug容器

在目录_hybris-hankport_hybris_bin_platform_tomcat_conf 中得wrapper-debug.conf 文件中可以看到相应的Debug方式的配置

如下的这一段可以看到相应的Hybris的Debug启动参数

    wrapper.java.additional.21=-Xdebug
    wrapper.java.additional.22=-Xnoagent
    wrapper.java.additional.23=-Xrunjdwp:transport=dt_socket,server=y,address=8000,suspend=n
    wrapper.java.additional.24=-Ddeployed.server.type=tomcat        

也可以修改这个参数,从这里可以看到相应的默认端口是8000端口

eclipse配置远程Debug

在Debug configuration中配置 Remote Java Application , 添加一个,提交相应的工程即可进行远程调试。

2015/6/5 posted in  java

Hybris 经验总结——Impex格式

Impex格式

mode type[modifier=value];attribute[modifier=value];attribute[modifier=value];attribute[modifier=value][;...];attribute[modifier=value]
  • mode :提供四种操作 insert、update、insert_update、remove等操作
  • type :定义处理的item类型,catagory,product,media,type等等
  • attribute :映射到对象的column属性
  • Modifier :

Header Mode

Insert : 在Hybris中创建一个item,Impex默认不检查是否存在相同属性的item
Update : 在hybris中通过一个unique的属性,选择一个存在的item,将属性值设置到相应的值上。

如果不存在对应的item,则当前value行不能处理

属性的默认值为null

例子:

UPDATE User;uid[unique=true];name;customerID;
;ahertz;Anja Hertz;;
;ahertz;;K2006-C0005;

可以使用下面三种方法之一来更新:

Specify all values in one single line:

UPDATE User;uid[unique=true];name;customerID;
;ahertz;Anja Hertz;K2006-C0005;

Re-specify all values that have been set in a prior line:

UPDATE User;uid[unique=true];name;customerID;
;ahertz;Anja Hertz;;
;ahertz;Anja Hertz;K2006-C0005;

Use individual headers:

UPDATE User;uid[unique=true];name;
;ahertz;Anja Hertz;
 
UPDATE User;uid[unique=true];custmerID;
;ahertz;K2006-C0005;
INSERT_UPDATE : 将insert和Update方式合并,
REMOVE : hybris会尝试寻找到正确的item,如果一个item存在,它将被删除。

Header Type

2015/5/24 posted in  java

Hybris 经验总结——2

hybris 学习总结2

Extension 内容

  • 访问http://localhost:9001/platform/extensions 可以查看到当前所有的加载的Extension。
  • 编译目录,在eclipse中得源文件会编译到eclipsebin目录中去,在系统环境ant中的编译会到classes目录中去,这样的好处是两个环境互相不会影响。

Data Model

  • -items.xml 在每个扩展的这个文件里面可以配置数据模型,在Hybris里面我们把实体模型成为item,我们把item之间的关系定义为关系元素relation elements
  • Hybris 系统会抽取所有Extension里面的*item.xml文件,并自动生成一系列的Java源文件来支持Hybris来访问这些实体。
  • 由item系统自动生成的类文件基本上会进入以下三个目录中: 模型类——用于开发业务逻辑;WenService相关类——使用Restful URI来支持CRUD逻辑,当在item文件中配置了扩展平台webservice的时候才会生成;低级别的Jalo类。

item文件内容,添加一个item业务对象

<?xml version="1.0" encoding="ISO-8859-1"?>
<!--
 [y] hybris Platform
 
 Copyright (c) 2000-2013 hybris AG
 All rights reserved.
 
 This software is the confidential and proprietary information of hybris
 ("Confidential Information"). You shall not disclose such Confidential
 Information and shall use it only in accordance with the terms of the
 license agreement you entered into with hybris.
 
-->
 
<items   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
            xsi:noNamespaceSchemaLocation="items.xsd">
    
    <itemtypes>
        <itemtype code="Stadium" generate="true" autocreate="true">
            <deployment table="CuppyTrailStadium" typecode="10123" />
            <attributes>
                <attribute qualifier="code" type="java.lang.String" >
                    <persistence type="property"/>
                    <modifiers optional="false" unique="true"/>
                </attribute>
                <attribute qualifier="capacity" type="java.lang.Integer">
                    <description>Capacity</description>
                    <persistence type="property" />
                </attribute>
            </attributes>
        </itemtype>
    </itemtypes>
</items>

添加对象间关系

<relations>
    <relation code="StadiumMatchRelation" localized="false" generate="true" autocreate="true">
       <sourceElement type="Stadium" qualifier="stadium" cardinality="one" />
       <targetElement type="Match" qualifier="matches" cardinality="many"/>
    </relation>
</relations> 
2015/5/21 posted in  java

Hybris 经验总结——1

Hybris 数据库、端口配置文件

配置文件地址: hybris-commerce-suite-5.2.0.1/hybris_bin_platform/project.properties
可提供配置: Hybris 端口、Hybris容器配置、数据库

如果使用MySQL的话,需要自己创建数据库

MySQL配置:

db.url=jdbc:mysql://localhost/hybris?useConfigs=maxPerformance&characterEncoding=utf8
db.driver=com.mysql.jdbc.Driver
db.username=root
db.password=stella
db.tableprefix=
db.customsessionsql=SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED;
mysql.optional.tabledefs=CHARSET=utf8 COLLATE=utf8_bin
mysql.tabletype=InnoDB
mysql.allow.fractional.seconds=true

Hybris 组合包的问题

Hybris分为 Module(accelerator)、extension、page、组件等。
其中最大的为accelerator,这个可以理解为每个大的加速器模板。模板下面为extension,每一个extension为一个类似插件的形式进行扩展,一个accelerator可以包含多个extension。

每个extension都含有 project.properties和extensioninfo.xml 两个配置文件。

  • project.properties配置每个extension的spring文件位置,url相关地址等extension相关规格。
  • extension.info配置模块间的引用关系

local.properties 全局配置文件,用于配置全局参数,这个文件会覆盖每个extension里面的配置,所以一般把数据库连接,tomcat配置等配置在这里。

localextensions.xml

系统的Initialization 和 Update

Initialization发生时:系统将会重建、所有的数据库信息将会重建、数据模型将会重建、建立新的数据库、之前的数据模型将会删除。
Update发生时:数据库将会更新到新的模型,数据不会丢失,将在数据库中添加新的定义类型,

Extension概念

  1. 用自己的数据模型和商务逻辑开发一个新的工程,不用于添加代码,提供更加方便的代码重用和整合到最新的版本
  2. Extension可以提供下面集中类型的扩展:business logic、Data model definition、web application。
  3. 新创建的extension默认不被其他的extension引用,除了platform以外
  4. extension的优势:方便整合和更新。
  5. extension之间可以互相依赖,但是不能循环依赖。
  6. 所有的extension默认依赖于平台
  7. 每一个extension默认是一个Eclipse工程
  8. 通过extensioninfo.xml来配置extension之间的依赖关系
  9. 对于bean的定义有三种定义:global-{ext-name}.xml 、 {ext-name}-spring.xml 、web-application-config.xml。其中配置在global中的bean会被所有的extension共享,配置在ext-name-spring中的bean同样也会被所有的extension共享同时对不同的租户会创建不同的实例;配置在web-application-config中的bean只对所选中的extension和tenant使用。

Mac下启动Hybris

导入环境变量

PLATFORM_HOME=pwd
export -p PLATFORM_HOME
export -p ANT_OPTS="-Xmx200m -XX:MaxPermSize=128M"
export -p ANT_HOME=\(PLATFORM_HOME/apache-ant-1.9.1
chmod +x "\)ANT_HOME_bin_ant"
export -p PATH=\(ANT_HOME/bin:\)PATH

启动Hybris服务器

    sh hybriserver.sh

各大Portal

使用当前域名可以进入到当前网站下的配置页面,使用127.0.0.1配置的为全局页面。

环境整理

把Ant换成 1.9 版本。

页面 —— structure*Template 页面模板

项目创建

  1. 新建一个module,module可以视为很多个accelerator的集合。创建Module(Accelerator)使用 ant modulegen,创建Extention的时候使用 ant genext.
  2. 创建完成之后可以修改相应的模块依赖,将Hybris目录下面的sampleconfigurations 中得配置文件拷贝到config目录下面的localextensions.xml文件中。然后将自己创建的extension添加到相应的extension中。
  3. 这里一定要注意的是一定要删除重复的accelerator extesion。否则进行ant clean all的时候会报错。
  4. 使用ant clean all进行重新发布。
  5. 发布之后可以修改item文件,修改item文件的目的是创建一个数据库持久化对象,并能对这个对象自动生成很多方法
  6. item中得itemtype分为两种,一种是自动生成新表的,可以加上<deployment table="OrganizationOrderStats" typecode="6214"/>,另外一种是可以生成在系统本身有的表里面的,不带这个字段。
  7. 添加完成itemtype之后Update一下即可。

item代码如下

<itemtype code="WilliamForTestItemProduct" extends="VariantProduct"
        autocreate="true" generate="true"
        jaloclass="org.training.core.jalo.WilliamForTestItemProduct">
         <description>William Test Powertools size variant type that contains additional attribute describing variant size.
        </description>
        <attributes>
            <attribute qualifier="size" type="localized:java.lang.String"
                       metatype="VariantAttributeDescriptor">
                <description>Size of the product.</description>
                <modifiers/>
                <persistence type="property"/>
            </attribute>
        </attributes>
    </itemtype>
  1. 修改core下面的hmc.xml

修改代码如下:

<explorertree mode="append">
    <group name="user" expand="false" description="group.user.description">
     <typeref type="WilliamForTestItemProduct" description="type.WilliamForTestItemProduct.name"/>
  </group>
</explorertree>

9.hmc提供两种修改方式,一种是通过自己generator一个extension来创建一个工程,然后在里面修改。另外一种方式是直接在Core的hmc里面直接修改。 hmc当前只支持单表的查询,如果需要支持多表查询则必须进行扩展。 hmc页面的总hmc-configuration选项是集合了所有的hmc文件的内容,实际上hmc的机制就是这样。

Product

ProductModel baseProduct
VariantProductModel 封装了baseProduct在里面

多语言支持在数据库的响应字段后面增加一个后缀为ip的数据库表 参考 productsip
ecache 缓存机制,看看是不是写到内存里面去了,不然的话肯定会慢。

itemtype里面可以增加多语言属性 增加一个type=localized:java.lang.string

itemtype 里面, 产品层级 BaseProduct —— WFJStyleVarientProducts —— WSJCounterVariantProduct
可以在varient 里面定义多级从而来定义产品的层级。

Catagory 把产品化类别

Catalog 绑定用户权限的东西, 作为最顶级的对象。 网站的用户级别。
Catagory 绑定下面的产品结构,网站的类目级别。

Classification 属性系统

classification 系统, 所有的商品属性组都是按照classification来做,如果把Product和Classification切断之后,Product就没有任何属性了。

在Classification中定义属性,这些属性还可以有继承关系。

产品价格 Price Rows price setting中

定义一个价格的有效期、范围。
产品 吊牌价、真实价格。
一个产品可以关联很多个price row , 真正起作用的根据关联属性,根据真是价格关联到Price Row。

一般都是一个产品对应一个或多个PriceRow,但是基本不会出现多个产品对应一个PriceRow的,根据业务需求来。
价格可以设置时间段来判断相应的价格。

权限的功能,在用户的后面添加一个select语句就可以进行相关权限限制。

HMC 开发

webchart 的开发元素

https://wiki.hybris.com/display/release5/How+To+Write+an+hMC+Editor+WebChip

https://wiki.hybris.com/display/release5/How+To+Create+a+Wizard

在hmc上面做一个excel导入的方法,让客户使用excel的方式导入产品。
联表查询
定制化显示字段
鼠标右键动态开发

ImpExImportJob 这个可以用于用代码导入Impex文件。

https://wiki.hybris.com/display/accdoc/B2C-Specific+Accelerator+Extensions

https://wiki.hybris.com/display/release5/Classification+Guide

https://wiki.hybris.com/display/release5/Modeling+Product+Variants

https://wiki.hybris.com/display/release5/Restrictions

https://wiki.hybris.com/display/release4/How+To+Create+Restrictions+Using+hybris+Management+Console+-+Tutorial

https://wiki.hybris.com/display/forum/User+Rights+Import

https://wiki.hybris.com/display/release5/How+To+Create+a+Wizard

https://wiki.hybris.com/display/accdoc/B2C-Specific+Accelerator+Extensions

hybris日志

platform 实际上是一个tomcat

查数据

一般是使用flaxibleSearch 来用代码查询数据
查询基本上全部都是使用这种方式来实现的。

提供一个单例的转换模式,使用DAO 查询DTO之后,使用一个单例的转换模式,然后转换成自己建的bean,用于页面展现。

用户密码可以自己做一套加密的算法和老系统保持一致,在添加用户的时候可以看到

PasswordEncode 类用于密码加解密。如果需要在HMC里面显示的话,需要做一次配置,在spring.xml里面配置,配置一个bean,用于专门的加密,持久化到数据库。

页面

cms2 Data model overview

cms2 Extension Tutorial

base store ---- website

cmscockpit 进入修改页面,使用自己的域名进去

首页位置
_Users_valentine_workspace_hybris-commerce-suite-5.5.0.0/hybris_bin_custom_training_trainingstorefront_web_webroot__ui_desktop

页面模板的位置
_Users_valentine_workspace_hybris-commerce-suite-5.5.0.0/hybris_bin_custom_training_trainingstorefront_web_webroot_WEB-INF_views

页面怎么做
https://wiki.hybris.com/display/release5/cms2+Extension+Tutorial

页面框架图
https://wiki.hybris.com/display/release5/cms2+-+Data+Model+Overview

如何增加一个新的CMS组件
https://wiki.hybris.com/display/release5/How+to+Add+a+New+CMS+Component

WCMS组件信息
https://wiki.hybris.com/display/accdoc/WCMS+Components

WCMS是一个编辑环境, 所有的展示东西都是模板里面,在WCMS改得时候

主页生效要在WCMS里面把生效点一下。

wiki : WCMS components 可以看到所有的组件接口

页面组件数据都是可以在jsp里面修改的,具体的进去搜索 navigatebar*.jsp

导入数据入口类
_Users_valentine_workspace_hybris-commerce-suite-5.5.0.0/hybris_bin_custom_training_traininginitialdata_src_org_training_initialdata_setup_InitialDataSystemSetup.java

_Users_valentine_workspace_hybris-commerce-suite-5.5.0.0/hybris_bin_custom_training_trainingcore_src_org_training_core_setup_CoreSystemSetup.java

自己的做法:
自己项目下面的,新建一个工程,自己写初始化类(像上面定义的)归结上面要倒的数据。然后加进去

wiki : customize initialization process

页面目录的内容管理:
_Users_valentine_workspace_hybris-commerce-suite-5.5.0.0/hybris_bin_ext-template_yacceleratorinitialdata_resources_yacceleratorinitialdata_import_coredata_contentCatalogs_catalogName_cms-content_zh.impex

wrapper

wrapper 把tomcat打一下,在tomcat奔溃的时候会自动重启

B2B

admincockpit 提供一个B2B admin管理方式

addonsupport
secureportaladdon
加上这两个扩展。

ant addoninstall -Daddonnames="..."
使用这个ant命令可以添加addon,实际上添加addon和普通的添加扩展是不一样的。

邮件需要配置一个邮件服务器。
注册完成之后,登陆hmc之后,在Enployment里面添加 审批 approval ,然后在admincockpit里面就可以看到审核消息。

客户核心以单位进行管理,用户必须要设置一个用户单位。

其他的cockpit使用zk的js架构去做扩展

project.properties 写入程序变量

查询

一般后台使用flexsearch查询

https://wiki.hybris.com/display/release5/FlexibleSearch

Update 和 Delete 效率比较低,插入较好。 设计的时候尽量的去做插入,少做Update和Delete。
做更新的时候,很多情况可以做两张表,一边用来做insert,然后不停的做查询。

wiki :MediaConversion

wiki 拦截器 :interceptors

https://wiki.hybris.com/display/accdoc/B2B

https://wiki.hybris.com/display/release5/Interceptors

动态多表查询 Query Hmc里面有

动态属性概念,用于多表查询的时候,把里面的某个字段(引用字段) 展示为多表之间的关系。

solr 全文检索系统,下面配置了lucene。

2015/5/6 posted in  java

spring基础

spring基础篇

面向接口编程: 设计良好的架构可保证系统重构的工作被封闭在重构的层内,绝对不影响其他层。

依赖注入: 在程序运行过程中,如果需要调用另外一个对象协助时,无须再代码中创建被调用者,而是依赖于外部的注入。
设值注入: 调用set方法进行注入
构造注入: 调用构造函数进行注入

实例化SpringIOC容器

Spring容器有两个接口: BeanFactory 和 ApplicationContext

这两个接口也被称为Spring 上下文

ApplicationContext又可分为:ClassPathXmlApplicationContext, FileSystemXmlApplicationContext, XmlWebApplicationContext, XmlPortletApplicationContext.

配置Spring Bean
  • 可以通过XML文件、属性文件、注释甚至API来配置Spring IOC中的Bean。
  • Spring允许一个或多个Bean配置文件来配置Bean。
  • 每个bean都应该提供一个唯一的名称或者id,以及一个完全限定的类名,用于让Spring IOC容器对其进行实例化。
  • 配置文件通过property方法来对应每个Set方法。
  • 配置文件通过constructor-arg方法来对应构造函数。

(2) BeanFactory
BeanFactory负责配置、创建及管理bean,它有个子接口:ApplicationContext,因此也被称为Spring上下文。
BeanFactory基本方法:
public boolean containsBean(string name)
public Object getBean(String name)
public Obejct getBean(String name, Class requiredType)
public Class getType(String name)
调用者只需使用getBean方法获得指定的bean的引用,无须关心bean的实例化过程。

 BeanFactory实现类,通常使用org.springframework.beans.factory.xml.XmlBeanFactory类。

但对于大部分J2EE应用而言,推荐使用ApplicationContext,其常用的实现类是org.springframework.context.support.FileSystem
XmlApplicationContext。

 在创建BeanFactory的时候,应该提供xml配置文件作为参数,XML配置文件通常使用Resource对象传入。
 对于大部分J2EE应用而言,可以在启动Web应用时自动加载ApplicationContext实例,接受Spring管理的Bean无须知道ApplicationContext的存在,也一样可以利用ApplicationContext的管理。
 如果应用里有多个属性配置文件,则应该采用BeanFactory的子接口ApplicationContext来创建BeanFactory的实例,ApplicationContext通常使用如下两个实现类。

• FileSystemXmlApplicationContext: 以指定路径的XML 配置文件创建ApplicationContext。
• ClassPathXmlApplicationContext: 以CLASSPATH路径下的XML配置文件创建ApplicationContext。

(3) Bean的基本定义
是spring配置的根元素, 是其子元素
在定义bean时,通常需要指定两个属性:
id 确定该bean所属的唯一值
class 指定该bean的具体实现类,通常情况下,spring 会直接使用new关键字创建该bean的实例。

(4) bean的行为方式
两种基本行为:
singleton: 默认行为
non-singleton 或 prototype:

 通常要求将Web 应用的控制器bean 配置成non-singleton行为。

(5) 创建bean实例
创建bean通常有如下方法:
调用构造器创建一个bean实例。
BeanFactory调用某个类的静态工厂方法创建bean。
BeanFactory调用实例工厂方法创建bean。

  1. 依赖关系配置
    (1)
2015/1/13 posted in  java

JVM深度剖析——类加载机制

虚拟机类加载机制

类加载机制

  • 虚拟机把描述类的数据从Class文件加载到内存,并对数据进行校验、转换解析和初始化,最终形成可以被虚拟机直接使用的Java类型。
  • Java类型的加载和链接过程都是在程序运行期间完成的,这样会在加载时稍微增加一些性能开销,但是却能为Java应用程序提供更高度的灵活性。Java中的动态扩展语言特性就是这个提供的。

类加载时机

  • 类加载的生命周期:加载(Loading)、验证(、准备、解析、初始化、使用和卸载。
2015/1/8 posted in  java

JVM深度剖析——类文件结构

类文件结构

平台无关性

  1. 平台无关性最终实现在操作系统的应用层上:Sun公司和其他虚拟机提供商发布了许多可以运行在不同操作系统上得虚拟机,这些虚拟机都可以从载入和执行同一种平台无关的字节码,从而实现程序的“一次编写,到处执行”。
  2. 实现语言无关性的基础仍然是虚拟机和字节码存储格式,使用Java编译器可以把Java代码编译为存储字节码的Class文件,使用Jruby等其他语言同样可以把程序代码编写成Class文件,虚拟机并不关心Class的来源是什么语言,只要它符合Class应有的结构就可以在Java虚拟机中运行。

Class类文件结构

class文件格式:

  • Class文件是一组以8位字节为基础单位的二进制流,各个项目紧密严格按照顺序紧凑的排列在Class文件中,中间没有添加任何分隔符。
  • Class文件格式只有两种数据类型:无符号数和表。无符号数属于基本数据类型,以u1、u2、u4、u8来代表1,2,4,8个字节的无符号数。无符号数可以用来描述数字、索引引用、数量值,或者按照UTF-8编码构成的字符串值。表是由多个无符号数或者其他表作为数据项构成的符合数据类型。所有表都习惯的以"_info"结尾。整个Class文件本质上就是一张表。
  • 1 魔数” : 每个Class的头4个字节称为魔数,它用于确定这个文件是否为一个能被虚拟机接受的Class文件。
  • Class文件的魔数: 0xCAFEBABE
  • 2 文件版本号“ : 魔数后面的4个字节存储的是Class文件的版本号,第5和第6个字节是次版本号,第7和第8个字节是主版本号。
  • 3 常量池” : 主次版本号之后的是常量池入口。常量池中的常量是不固定的,所以常量池的入口需要放置一项u2类型的数据,代表常量池容量计数值(constant_pool_count)
  • 常量池中主要放置两大类常量:字面量(Literal)和符号引用(Symbolic Reference),字面量代表文本字符串、被声明为final的常量值等。符号引用则属于编译原理方面的概念,主要包括:类和接口的全限定名、字段的名称和描述符、方法的名称和描述符。
  • 常量池中得每一项常量都是一个表,共有11种结构各不相同的数据结构,这11种表开始的第一位都是一个u1类型的标志位代表当前常量属于哪一种常量类型。这11种类型均各自有各自的结构。

字段表数据结构:

    Field_info{
        u2 access_flags;
        u2 name_index;
        u2 descriptor_index;
        u2 attributes_count;
        attribute_info attributes;
    }

其中access_flags [ACC_PUBLIC, ACC_PRIVATE, ACC_PROTECTED, ACC_STATIC, ACC_FINAL, ACC_VOLATILE, ACC_TRANSIENT, ACC_SYNTHETIC, ACC_ENUM]。

name_index 字段的简单名称,没有类型和参数修饰的方法或字段名称。
descriptor_index 字段和方法的描述符,用于描述字段的数据类型、方法的参数列表(数量、类型及顺序)和返回值。
attributes_count 和 attributes 用于描述一些其他属性。

字段表中不会列出从超类或者父类接口中继承的字段,但有可能列出原来java代码中不存在的字段。

在Java语言中字段是无法重载的,两个字段的数据类型,修饰符不管是否相同,都必须使用同样的名称。但对于字节码来说,如果两个字段的描述符不一致,那字段重名就是合法的。

  • 7 方法表集合“ : Class文件存储格式中对方法的描述与对字段的描述几乎采用了完全一致的方式。

方发表结构:

    method_info{
        u2 access_flags;
        u2 name_index;
        u2 descriptor_index;
        u2 attributes_count;
        attribute_info attributes;
    }

access_flags [ACC_PUBLIC, ACC_PRIVATE, ACC_PROTECTED, ACC_STATIC, ACC_FINAL, ACC_SYNCHRONIZED, ACC_BRIDGE, ACC_VARARGS, ACC_NATIVE, ACC_ABSTRACT, ACC_STRICT, ACC_SYNTHETIC]

方法的代码经过编译器编译成字节码之后,存放在方法属性表集合中一个命名为"Code"的属性里面。

如果父类方法在子类中没有重写,方法表集合中就不会出现来自父类的方法信息。但有可能出现由编译器自动添加的方法,如方法。

重载的判断 : 要重载Overload 一个方法,除了要与原方法具有相同的简单名称之外,还要求必须有一个与原方法不同的特征签名,特征签名就是一个方法中各个参数在常量池中得字段符号引用的集合。由于返回值不包含在特征签名之中,因此Java语言里面无法仅仅依靠返回值的不同来对一个已有的方法进行重载的。 但是在Class文件中,如果两个方法有相同的名称和特征签名,但返回值不同,依然可以合法的共同存在。

  • 8 属性表集合” : 属性表不要求严格的顺序,并且只要不与已有的属性名重复,任何人实现的编译器都可以向属性表中写入自己定义的属性信息,Java虚拟机运行时会忽略掉它不认识的属性。
2015/1/6 posted in  java

JVM深度剖析——垃圾收集器与内存分配策略

Jvm深度剖析

垃圾收集器与内存分配策略

几种基本垃圾收集算法:
引用计数法(Reference Counting)
  1. 给对象中添加一个引用计数器,每当有一个地方引用它时,计数器值就加1;当引用失效时,计数器值就减1;任何时刻引用计数器为0就代表对象不可能再被使用的。
  2. 引用计数器未在Java中使用的主要原因是它很难解决对象之间的相互循环引用的问题。
根搜索算法(GC Rooting Tracing)
  1. 通过一系列名为"GC Routes"的对象作为起始点,从这些节点开始向下搜索,搜索所走过的路径成为引用链(Reference Chain),当一个对象到GC Roots 没有任何引用链相连时,则证明对象是不可用的。
  2. Java语言里,可以作为GC Roots的对象包括:虚拟机栈中引用的对象、方法区中得类静态属性引用的对象、方法区中常亮引用的对象、本地方法栈中JNI的引用对象
引用

JDK引用概念将引用分为强引用(Strong Reference)、软引用(Soft Reference)、弱引用(Weak Reference)、虚引用(Phantom Reference)四种,这四种引用强度依次逐渐减弱。

  1. 强引用:在程序代码中普遍存在的引用 Object obj = new Object()。只要强引用还存在,垃圾收集器永远不会回收掉被引用的对象。
  2. 软引用:描述一些还有用,但是非必须得对象。对于软引用关联着的对象,在系统将要发生内存溢出异常时,将会把这些对象列进回收范围之中并进行第二次回收。如果回收还是没有足够的内存,才会抛出内存溢出异常。
  3. 弱引用:被弱引用关联的对象只能生存到下一次垃圾收集发生之前。当垃圾收集器工作时,无论当前内存是否足够,都会回收掉只被弱引用关联的对象。
  4. 虚引用:也称为幽灵引用或者幻影引用。一个对象是否有虚引用的存在,完全不会对其生存时间构成影响。也无法通过虚引用来取得一个对象实例。为一个对象设置虚引用关联的唯一目的就是希望能在这个对象被收集器回收时受到一个系统通知。
finalize()方法

在根搜索算法中不可达的对象,处于缓刑状态,要真正宣告一个对象死亡,至少要经历两次标记过程:第一次标记和筛选条件是对象是否有必要执行finalize()方法。当对象没有覆盖finalize()方法或者finalize()方法已经被虚拟机调用过,虚拟机将这两种情况视为“没有必要执行”。如果对象被判定为有必要执行finalize()方法,那么这个对象将会被放置在一个名为F-Queue的队列中,并稍后由一条虚拟机自动建立的,低优先级的Finalizer线程去执行。

finalize的运行代价较高,不确定性非常大,无法保证各个对象的调用顺序。finalize能做的工作,使用try-finally或其他方法可以做得更好、更及时,尽量避免使用这个方法。

回收方法区

永久代的垃圾收集主要回收两个部分:废弃常量和无用的类。
废弃常量: 当系统中没有任何一个对象引用此常量,就会被系统请出常量池
无用的类: 1.该类所有的实例都已被回收,也就是Java堆中不存在该类的任何实例。2.加载该类的ClassLoader已经被回收。3.该类对应的java.lang.Class对象没有在任何地方被引用,无法在任何地方通过反射访问该类的方法。

是否进行类回收,HotSpot虚拟机提供了-Xnoclassgc参数进行控制。
-Xnoclassgc 禁用class垃圾收集
-verbose:class -XX:+TraceClassLoading、-XX:+TraceClassUnLoading查看类的加载和卸载信息。

在大量使用反射、动态代理、CGLib等bytecode框架的场景,以及动态生成JSP和OSGI这类频繁自动以ClassLoader的场景都需要虚拟机具备类卸载的功能,以保证永久代不会溢出。

垃圾收集算法
标记-清除算法

首先标记出所有需要回收的对象,在标记完成后统一回收掉所有被标记的对象。

缺点:一是效率问题,标记和清除过程效率不高。二是空间问题,标记和清除之后会产生大量不连续的内存碎片,空间碎片太多可能会导致当程序在以后的运行过程中需要分配较大对象时无法找到足够的连续内存空间而不得不提前触发另一次的垃圾收集动作。

复制算法

将可用内存按容量划分为大小相等的两块,每次只使用其中的一块。当这一块内存用完了,就将还存活着的对象复制到另外一块上面,然后再把已使用过的内存空间依次清理掉。

缺点:内存缩小为原来的一半,代价太高。

现代商业虚拟机使用复制算法来回收新生代,对于新生代中98%的对象是朝生夕死的,不需要按照1:1的比例来划分空间,二是将内存划分为一块较大的Eden空间和两块较小的Survivor空间。每次使用Eden空间和其中一块Survivor空间。当回收时,将Eden和Survivor空间存活着的对象一次性拷贝到另外一块Survivor空间上,最后清理掉Eden和Survivor空间。

HotSpot虚拟机默认Eden和Survivor的大小比例是8:1。

如果另外一块Survivor空间没有足够的空间存放上一次新生代收集下来的存活对象,这些对象将直接通过分配担保机制进入老年代。

标记-整理算法

标记过程和标记-清除算法一样,但后续步骤不是直接对可回收对象进行清理,二是让所有存活的对象都向一端移动,然后直接清理掉端边界以外的内存。

分代收集算法(Generational Collection)

当前商业虚拟机都使用分代收集算法。一般是把Java堆分为新生代和老年代,根据各个代的特点采用最适当的收集算法。

垃圾收集器
Serial收集器
  1. 单线程收集器。在垃圾收集时,必须暂停其他所有工作线程(Stop The World)。
  2. 现代虚拟机运行在Client模式下得默认新生代收集器。桌面应用场景中分配虚拟机内存一般只有几十到一两百兆的新生代,停顿时间可以控制在几十毫秒之内。
ParNew收集器
  1. Serial收集器的多线程版本。除了使用多条线程以外,其余行为包括Serial收集器可用得所有控制参数、收集算法、Stop The World、对象分配规则、收回策略等都与Serial收集器完全一样。
  2. 许多运行在Server模式下得虚拟机中首选的新生代收集器
  3. 目前只有它能与CMS收集器配合工作。
  4. ParNew收集器在单CPU环境下绝对不会有比Serial更好的效果。
Parallel Scavenger收集器
  1. 新生代收集器,使用复制算法的收集器,又是并行的多线程收集器。
  2. Parallel Scavenger收集器的目的是达到一个可控制的吞吐量。吞吐量=运行用户代码时间/(运行用户代码时间+垃圾收集时间)
  3. 提供两个参数: -XX:MaxGCPauseMills 设置最大垃圾收集停顿时间 -XX:GCTimeRatio 设置吞吐量大小
  4. GC自适应调节策略(GC Ergonomics) : -XX:+UseAdaptiveSizePolicy 指定不需要手工指定新生代的大小(-Xmm)、Eden和Survival(-XX:SurvivorRatio)、晋升老年代对象年龄(-XX:PretenureSizeThreshold)等细节参数。虚拟机会根据当前系统的运行情况收集性能监控信息,动态调整这些参数以提供最合适的停顿时间或最大的吞吐量。
  5. 自适应调节是Parallel Scavenger收集器与ParNew收集器的一个重要区别。
Serial Old收集器
  1. Serial收集器的老年代版本。同样是一个单线程收集器,使用"标记-整理"算法。
  2. 在Client模式下的虚拟机使用。
  3. 在Server模式下:在JDK1.5之前的版本中与Parallel Scavenger收集器搭配使用,另一个就是作为CMS收集器的后备预案,在并发收集发生Concurrent Mode Failure的时候使用。
Parallel Old收集器
  1. Parallel Scavenger收集器的老年代版本,使用多线程和"标记-整理"算法。
  2. 在吞吐量优先的情况下,在注重吞吐量及CPU资源敏感的场合,都可以优先考虑Parallel Scavenger加Parallel Old收集器。
CMS收集器
  1. CMS(Concurrent Mark Sweep)收集器是一种以获取最短回收停顿时间为目标的收集器。
  2. 基于"标记-清除"算法实现
  3. 步骤:初始标记(CMS initial mark)、并发标记(CMS concurrent mark)、重新标记(CMS remark)、并发清除(CMS concurrent sweep)。
  4. 初始标记、重新标记这两个步骤任然需要"Stop the World"。初始标记仅仅标记一下GC Roots能直接关联到得对象,速度很快。并发标记阶段就是进行GC Roots Tracing的过程。重新标记阶段则是为了修正并发标记期间,用户因程序继续运行而导致标记产生变动的那一部分对象的标记记录。由于整个过程中最长的并发标记和并发清除过程中,收集线程都可以与用户线程一起工作,所以总体上来说,CMS收集器的内存回收过程是与用户线程一起并发地执行的。
  5. 缺点: 1.对CPU资源非常敏感,会因为占用一部分线程资源导致应用程序变慢。2. 无法处理浮动垃圾,可能出现Concurrent Mode Failure失败而导致另一次Full GC 产生。3. 标记清除算法会形成大量碎片,空闲碎片过多时,将会给大对象分配带来麻烦,造成往往老年代还有很大的空间剩余,但是由于无法找到足够大的连续空间来分配对象,不得不提前触发一次Full GC。
G1收集器
  1. 基于标记-整理算法实现的收集器。
  2. 可以做到基本不牺牲吞吐率的前提下完成低停顿的回收工作。
垃圾收集参数

可以参考这篇文章 http://blog.sina.com.cn/s/blog_4080505a0101i6cr.html

MinorGC和Full GC的区别

新生代GC(Minor GC)指发生在新生代的垃圾收集动作。MinorGC非常频繁,且回收速度一般较快。
老年代GC(Major GC/Full GC)指发生在老年代的垃圾收集动作。出现了Major GC,经常会伴随着至少一次的Minor GC(但非绝对的)。MajorGC的速度一般会比MinorGC慢10倍以上。

Serial/Serial Old收集器下得内存分配和回收机制
  1. 对象优先在新生代Eden区中分配。当Eden区没有足够的空间进行分配时,虚拟机将发起一次MinorGC。
  2. -XX:+printGCDetails 这个参数收集日志参数,告诉虚拟机在发生垃圾收集行为时打印内存回收日志,并且在进程退出的时候输出当前内存各区域的分配情况。内存回收日志一般是打印到文件后通过日志工具进行分析。
  3. 需要大量连续内存空间的对象,就是所谓的大对象直接进入老年代
  4. 大对象对虚拟机的内存分配来说是一个坏消息(比遇到一个大对象更加坏得消息是遇到一群朝生夕灭的短命大对象),写程序时应尽量避免,经常出现大对象容易导致内存还有不少空间时就提前触发垃圾收集以获取足够的连续空间来安置他们。
  5. 虚拟机提供一个-XX:PretenureSizeThrehold参数,另大于这个值得对象直接在老年代中分配。目的是避免大对象在Eden区和Survivor区之间发生大量的内存拷贝。
  6. 长期存活的对象将进入老年代。虚拟机给每个对象定义了一个对象年龄Age计数器。如果对象在Eden出生并经过第一次MinorGC后仍然存活,并且能被Survivor容纳的话,将被移动到Survivor空间中,并将对象年龄设为1 。对象在Survivor区中每熬过一此MinorGC,年龄就增加1岁,当它的年龄增加到一定程度(默认15岁)时,就会被晋升到老年代中。
  7. -XX:MaxTenuringThreshold来设置对象晋升老年代的年龄阈值。
  8. 动态年龄判定,虚拟机并不是总要求对象的年龄必须达到MaxTenuringThreshold才能晋升老年代,如果在Survivor中空间中相同年龄所有对象大小的总和大于Survivor空间的一半,年龄大于或等于该年龄的对象就可以直接进入老年代。
  9. 空间分配担保(在MinorGC时如果Survivor空间无法容纳内存回收后的新生代对象,则需要老年代进行这样的担保),在发生MinorGC时,虚拟机会检测之前每次晋升到老年代的平均大小是否大于老年代的剩余空间大小,如果大于,则改为一次FullGC。如果小于,则查看HandlePromotionFailure设置是否允许担保失败,如果允许则进行MinorGC,如果不允许则进行一次FullGC。

GC 日志和参数定义

如果希望查看程序的GC日志,需要在启动参数中增加参数-verbose:gc ,这个开关用于显示GC内容。可以显示最忙和最空闲收集行为发生的时间、收集前后的内存大小、收集需要的时间等。

-XX:+printGCdetails : 输出详细GC日志,可以详细了解GC中的变化。

-XX:+PrintGCTimeStamps : 输出GC的时间戳(以基准时间的形式),可以了解这些垃圾收集发生的时间,自JVM启动以后以秒计量。

-XX:+PrintGCDateStamps : 输出GC的时间戳(以日期的形式,如 2013-05-04T21:53:59.234+0800)

-XX:+PrintHeapAtGC : 在进行GC的前后打印出堆的信息, 了解堆的更详细的信息。

-XX:=PrintTenuringDistribution : 开关了解获得使用期的对象权。

-Xloggc:$CATALINA_BASE/logs/gc.log : gc日志产生的路径

-XX:+PrintGCApplicationStoppedTime : 输出GC造成应用暂停的时间

也可以使用一些离线工具来进行GC日志的具体分析。比如sun的gchistogcviewer,这些都是开源的工具,用户可以直接通过版本控制工具下载其源码,进行离线分析。

Sun 垃圾收集实现

实现代码

/**
*参数: -verbose:gc -Xms20M -Xmx20M -Xmn10M -XX:SurvivorRatio=8 -XX:+PrintGCDetails -XX:+PrintGCDateStamps -XX:+PrintHeapAtGC
*/
private static final int _1MB = 1024 * 1024;
public static void main(String[] args) {
    byte[] allocation1, allocation2, allocation3, allocation4, allocation5;
    allocation1 = new byte[2 * _1MB];
    allocation2 = new byte[2 * _1MB];
    allocation3 = new byte[2 * _1MB];
    System.out.println("allocation1 = " + allocation1 + "\n allocation2 = " +allocation2 + "\n allocation3 = " + allocation3);
    allocation4 = new byte[4 * _1MB]; // 此时出现一次MinorGC
    //allocation1 = null;
    allocation5 = new byte[2 * _1MB];
    //byte[] allocation6 = new byte[6 * _1MB];
}

返回结果:

allocation1 = [B@150697e2
 allocation2 = [B@2a36bb87
 allocation3 = [B@4fd281f1
{Heap before GC invocations=1 (full 0):
 PSYoungGen      total 9216K, used 7143K [0x00000007ff600000, 0x0000000800000000, 0x0000000800000000)
  eden space 8192K, 87% used [0x00000007ff600000,0x00000007ffcf9eb0,0x00000007ffe00000)
  from space 1024K, 0% used [0x00000007fff00000,0x00000007fff00000,0x0000000800000000)
  to   space 1024K, 0% used [0x00000007ffe00000,0x00000007ffe00000,0x00000007fff00000)
 ParOldGen       total 10240K, used 4096K [0x00000007fec00000, 0x00000007ff600000, 0x00000007ff600000)
  object space 10240K, 40% used [0x00000007fec00000,0x00000007ff000010,0x00000007ff600000)
 PSPermGen       total 21504K, used 2590K [0x00000007f9a00000, 0x00000007faf00000, 0x00000007fec00000)
  object space 21504K, 12% used [0x00000007f9a00000,0x00000007f9c87a70,0x00000007faf00000)
2015-01-05T15:18:41.818-0800: [GC-- [PSYoungGen: 7143K->7143K(9216K)] 11239K->15335K(19456K), 0.0048480 secs] [Times: user=0.01 sys=0.00, real=0.01 secs] 
Heap after GC invocations=1 (full 0):
 PSYoungGen      total 9216K, used 7143K [0x00000007ff600000, 0x0000000800000000, 0x0000000800000000)
  eden space 8192K, 87% used [0x00000007ff600000,0x00000007ffcf9eb0,0x00000007ffe00000)
  from space 1024K, 0% used [0x00000007fff00000,0x00000007fff00000,0x0000000800000000)
  to   space 1024K, 40% used [0x00000007ffe00000,0x00000007ffe68020,0x00000007fff00000)
 ParOldGen       total 10240K, used 8192K [0x00000007fec00000, 0x00000007ff600000, 0x00000007ff600000)
  object space 10240K, 80% used [0x00000007fec00000,0x00000007ff400030,0x00000007ff600000)
 PSPermGen       total 21504K, used 2590K [0x00000007f9a00000, 0x00000007faf00000, 0x00000007fec00000)
  object space 21504K, 12% used [0x00000007f9a00000,0x00000007f9c87a70,0x00000007faf00000)
}
{Heap before GC invocations=2 (full 1):
 PSYoungGen      total 9216K, used 7143K [0x00000007ff600000, 0x0000000800000000, 0x0000000800000000)
  eden space 8192K, 87% used [0x00000007ff600000,0x00000007ffcf9eb0,0x00000007ffe00000)
  from space 1024K, 0% used [0x00000007fff00000,0x00000007fff00000,0x0000000800000000)
  to   space 1024K, 40% used [0x00000007ffe00000,0x00000007ffe68020,0x00000007fff00000)
 ParOldGen       total 10240K, used 8192K [0x00000007fec00000, 0x00000007ff600000, 0x00000007ff600000)
  object space 10240K, 80% used [0x00000007fec00000,0x00000007ff400030,0x00000007ff600000)
 PSPermGen       total 21504K, used 2590K [0x00000007f9a00000, 0x00000007faf00000, 0x00000007fec00000)
  object space 21504K, 12% used [0x00000007f9a00000,0x00000007f9c87a70,0x00000007faf00000)
2015-01-05T15:18:41.823-0800: [Full GC [PSYoungGen: 7143K->2343K(9216K)] [ParOldGen: 8192K->8192K(10240K)] 15335K->10535K(19456K) [PSPermGen: 2590K->2589K(21504K)], 0.0105960 secs] [Times: user=0.02 sys=0.00, real=0.01 secs] 
Heap after GC invocations=2 (full 1):
 PSYoungGen      total 9216K, used 2343K [0x00000007ff600000, 0x0000000800000000, 0x0000000800000000)
  eden space 8192K, 28% used [0x00000007ff600000,0x00000007ff849d00,0x00000007ffe00000)
  from space 1024K, 0% used [0x00000007fff00000,0x00000007fff00000,0x0000000800000000)
  to   space 1024K, 0% used [0x00000007ffe00000,0x00000007ffe00000,0x00000007fff00000)
 ParOldGen       total 10240K, used 8192K [0x00000007fec00000, 0x00000007ff600000, 0x00000007ff600000)
  object space 10240K, 80% used [0x00000007fec00000,0x00000007ff400030,0x00000007ff600000)
 PSPermGen       total 21504K, used 2589K [0x00000007f9a00000, 0x00000007faf00000, 0x00000007fec00000)
  object space 21504K, 12% used [0x00000007f9a00000,0x00000007f9c876a0,0x00000007faf00000)
}
Heap
 PSYoungGen      total 9216K, used 4801K [0x00000007ff600000, 0x0000000800000000, 0x0000000800000000)
  eden space 8192K, 58% used [0x00000007ff600000,0x00000007ffab04b0,0x00000007ffe00000)
  from space 1024K, 0% used [0x00000007fff00000,0x00000007fff00000,0x0000000800000000)
  to   space 1024K, 0% used [0x00000007ffe00000,0x00000007ffe00000,0x00000007fff00000)
 ParOldGen       total 10240K, used 8192K [0x00000007fec00000, 0x00000007ff600000, 0x00000007ff600000)
  object space 10240K, 80% used [0x00000007fec00000,0x00000007ff400030,0x00000007ff600000)
 PSPermGen       total 21504K, used 2599K [0x00000007f9a00000, 0x00000007faf00000, 0x00000007fec00000)
  object space 21504K, 12% used [0x00000007f9a00000,0x00000007f9c89e30,0x00000007faf00000)

可以根据这个了解到部分Sun Java虚拟机GC实现细节:

堆被划分成三个不同的区域:新生代 ( PSYoungGen )、老年代 ( ParOldGen )、永久代( PsPermGen)。

新生代 ( Young ) 又被划分为三个区域:
Eden、From Survivor、To Survivor。
这样划分的目的是为了使 JVM 能够更好的管理堆内存中的对象,包括内存的分配以及回收。 其中的比例默认为8:1:1, 也可以使用参数修改。

新生代和老年代的比例一般为1:2, 也可以自己通过参数定义。
JVM 每次只会使用 Eden 和其中的一块 Survivor 区域来为对象服务,所以无论什么时候,总是有一块 Survivor 区域是空闲着的。

SUN JVM GC 使用是分代收集算法,即将内存分为几个区域,将不同生命周期的对象放在不同区域里。新的对象会先生成在Young area,也就是PSYoungGen中。在几次GC以后,如过没有收集到,就会逐渐升级到PSOldGen 及Tenured area(也就是PSPermGen)中。

PSYoungGen ParOldGen PsPermGen区别

在GC收集的时候,频繁收集生命周期短的区域(Young area),因为这个区域内的对象生命周期比较短,GC 效率也会比较高。而比较少的收集生命周期比较长的区域(Old area or Tenured area),以及基本不收集的永久区(Perm area)。

2014/12/22 posted in  java

JVM深度剖析——内存溢出分析方法

Java深度剖析——内存溢出分析方法

工具

安装Memory Analyse Tools(MAT) 工具, 可以直接在eclipse中安装其相应的插件,安装方法可以参考另一篇eclipse插件汇总

不会用的可以参考一下这个帖子使用 Eclipse Memory Analyzer 进行堆转储文件分析

一些Java内存参数设置

-vmargs: 说明后面是VM的参数,所以后面的其实都是JVM的参数了

-Xms20m: Java初始分配的堆内存,此处设置为20M
-Xmx20m: Java最大允许分配的堆内存,此处设置为20M,同时这样设置表示堆内存不许扩展

-XX:PermSize=64M: JVM初始分配的非堆内存
-XX:MaxPermSize=128M: JVM最大允许分配的非堆内存,按需分配

-XX:+HeapDumpOnOutOfMemoryError: 让虚拟机在出现内存溢出异常时dump出当前内存堆转储快照以便事后进行分析。
带上这种参数之后运行Jvm,如果出现相应的内存溢出异常,会在目录下形成一个异常时候的内存dump文件(如java_pid7126.hprof文件),将这个文件使用Memory Analyse Tool工具打开就可以看到当前dump内存空间的分析内容。

-XX:+HeapDumpOnCtrlBreak:

-Xss: 设置虚拟机栈内存容量

获得转储文件的一些方法:

  1. 使用JVM启动时的参数设置,如需要在内存溢出时才获取Dump可以使用-XX:+HeapDumpOnOutOfMemoryError 或者是希望在某个特定时间获取可以使用 -XX:+HeapDumpOnCtrlBreak
  2. 使用一些Java工具获取 ,如 JMap,JConsole 都可以帮助我们得到一个堆转储文件。

MAT工具的一些使用心得:

文件目录

使用MAT工具打开获取的java_pid7126.hprof文件后,会自动的形成如下的文件目录:

java_pid7126.a2s.index    
java_pid7126.domIn.index
java_pid7126.domOut.index
java_pid7126.hprof              //转储堆文件
java_pid7126.idx.index
java_pid7126.inbound.index
java_pid7126.index
java_pid7126.o2c.index
java_pid7126.o2hprof.index
java_pid7126.o2ret.index
java_pid7126.outbound.index
java_pid7126.threads
java_pid7126_Leak_Suspects.zip   //打包的报告,解压后会形成一个有HTML方式的报告发送给其他人,方便读取
分析方法
分析三步曲:
  1. 对问题发生时刻的系统内存状态获取一个整体印象。
  2. 找到最有可能导致内存泄露的元凶,通常也就是消耗内存最多的对象
  3. 进一步去查看这个内存消耗大户的具体情况,看看是否有什么异常的行为。
具体的分析:
查看报告一:内存消耗的整体状况

在OverView上的Report中可以选择 Leak Suspects ,来查看整体对象消耗。下方有一个警告可以看到当前系统自动帮忙分析的怀疑对象。在这个怀疑对象中便可以发现大多数的问题。

查看报告二:分析问题的所在

分析对象为什么没有被回收从而导致一直占用内存。采用根搜索算法来分析对象的事情情况。
点击报告一种怀疑对象的Detail,可以看到相应的具体分析报告。

Shortest Paths To the Accumulation Point 分析GC根元素到内存消耗聚集点的最短路径。
Accumulated Objects 查看具体的内存对象信息。

几种经常出现的内存溢出方式

Java堆溢出

异常信息: "java.lang.OutOfMemoryError"。会跟着进一步提示Java heap space.

java.lang.OutOfMemoryError: Java heap space
Dumping heap to java_pid1293.hprof ...
Heap dump file created [27559990 bytes in 0.233 secs]
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
at java.util.Arrays.copyOf(Arrays.java:2245)
at java.util.Arrays.copyOf(Arrays.java:2219)
at java.util.ArrayList.grow(ArrayList.java:242)
at java.util.ArrayList.ensureExplicitCapacity(ArrayList.java:216)
at java.util.ArrayList.ensureCapacityInternal(ArrayList.java:208)
at java.util.ArrayList.add(ArrayList.java:440)
at com.valentine.jvm.analyzer.exception.HeapOOM.main(HeapOOM.java:16)

Java堆溢出内存问题分析步骤总结:

  1. dump出来堆转储快照
  2. 使用MAT工具对dump出来的堆转储快照进行分析,重点是确认内存中得对象是否必要的,这样可以分清楚到底是出现了内存泄露(Memory Leak)还是内存溢出(Memory Overflow)
  3. 如果是内存泄露,进一步通过MAT工具分析泄露对象到GC Roots的引用链。找到泄露对象是通过怎样的路径与GC Roots相关联并导致垃圾收集器无法自动回收的。
  4. 如果不存在泄露,那么就是内存中得对象却是都还必须活着,就应当检查虚拟机的堆参数(-Xmx与-Xms),与机器物理内存对比看是否可以调大,从代码上检查是否存在某些对象生命周期过长、持有状态时间过长的情况,尝试减少程序运行期间的内存。
虚拟机和本地方法栈溢出

在HotSpot虚拟机中并不区分虚拟机栈和本地方法栈。到对于HostSpot来说-Xoss参数(设置本地方法栈大小)是存在的,但实际是无效的,栈容量只由-Xss参数决定。

异常情况:

  • 如果线程请求的栈深度大于虚拟机所允许的最大深度,将抛出StackOverflowError异常
  • 如果虚拟机在扩展栈时无法申请到足够的内存空间时,将抛出OutOfMemoryError异常

异常信息:

stack length:1891Exception in thread "main" 
java.lang.StackOverflowError
    at com.valentine.jvm.analyzer.exception.JavaVmStackSOF.stackLeak(JavaVmStackSOF.java:8)
    at com.valentine.jvm.analyzer.exception.JavaVmStackSOF.stackLeak(JavaVmStackSOF.java:9)
    at com.valentine.jvm.analyzer.exception.JavaVmStackSOF.stackLeak(JavaVmStackSOF.java:9)
    ……
    at com.valentine.jvm.analyzer.exception.JavaVmStackSOF.stackLeak(JavaVmStackSOF.java:9)     

在单线程下,无论是由于栈帧太小还是虚拟机栈容量太小,当内存无法分配的时候,虚拟机抛出的都是StackOverflowError异常。
在多线程下,通过不断的建立线程的方式可以产生内存溢出OutOfMemoryError异常。

在多线程情况下,给每个线程分配的内存越大,越容易产生内存溢出异常。由于操作系统分配给每个进程的内存是有限的,32位的Windows限制为2GB。虚拟机提供了参数来控制Java堆和方法区的这两部分内存的最大值。剩余的2GB减去Xmx,再减去MaxPermSize,忽略掉很小的程序计数器内存。如果虚拟机进程本身耗费的内存不计算,剩下的内存就是有虚拟机栈和本地方法栈瓜分了。此时如果每个线程分配到的虚拟机栈容量越大,可以建立的线程数量自然就越少,建立线程时就越容易把剩下的内存耗尽。
如果建立过多线程导致的内存溢出,在不能减少线程数或者更换64位虚拟机的情况下,就只能通过减少最大堆或者减少栈容量来获取更多的线程。

出现StackOverflowError的时候有错误堆栈可以读,即时加入+HeapDumpOnOutOfMemoryError也不会dump异常堆内存。

运行时常量池溢出

如果要项运行时常量池中添加内容,最简单的方法就是使用String.intern()这个Native方法。

异常信息:

java.lang.OutOfMemoryError: PermGen space
Dumping heap to java_pid1582.hprof ...
Heap dump file created [625411 bytes in 0.018 secs]
Exception in thread "Reference Handler" Error occurred during initialization of VM
java.lang.OutOfMemoryError: PermGen space
    <<no stack trace available>>    

Exception: java.lang.OutOfMemoryError thrown from the UncaughtExceptionHandler in thread "Reference Handler"

从异常中可以看出OutOfMemoryError后报的是PermGen space,说明是方法区溢出。

方法区溢出

方法区用于存放Class的相关信息,如类名、访问修饰符、常量池、字段描述、方法描述等。当大量的类产生时填满方法区,会造成方法去溢出。

方法区溢出也是一种常见内存溢出异常,一个类如果要被垃圾收集器回收,判断条件非常苛刻。

本机直接内存溢出

DirectMemory容量可通过-XX:MaxDirectMemorySize指定,如果不指定,则默认与Java堆的最大值(-Xmx)一样。

2014/12/22 posted in  java

eclipse 插件汇总

2014/12/19 posted in  java

JVM深度剖析——内存区域

jvm 深度剖析

jvm内存区域

Java虚拟机运行时数据区

程序计数器(Program Counter Register):
  1. 较小的内存空间,是当前线程所执行的字节码的行号指示器;
  2. 线程私有的,每个线程都需要一个独立的计数器,每个计数器之间都互不影响,独立存储;
  3. 程序计数器内存区域是Java虚拟机规范中唯一没有规定任何OutOfMemory情况的区域。
Java虚拟机栈(Java Virtual Machine Stacks):
  1. 线程私有的,它的生命周期与线程相同;
  2. 每个方法被执行的时候都会同时创建一个栈帧用于存储局部变量表、操作栈、动态链接、方法出口等信息。每个方法被调用直至执行完成的过程,就对应着一个栈帧在虚拟机栈中从入到出栈的过程;
  3. 局部变量表存放编译期间可知的各种基本数据类型(boolean、byte、char、short、int、float、long、double)对象引用(Reference类型,根据不同的Java虚拟机实现,可能是一个指向对象起始地址的引用指针,也可能是一个代表对象句柄或者其他于此对象相关的位置)和returnAddress类型(指向一条字节码指令的地址);
  4. 局部变量表所需的存储空间在编译期间完成分配。其中64位长度的long和double会占用2个局部变量空间,其余的数据类型只占用1个。在方法运行期间不会改变局部变量表的大小;
  5. Java虚拟机栈区定义了两种异常状况:如果线程请求的栈深度大于虚拟机所允许的深度,将抛出StackOverflowError异常;如果虚拟机栈可以动态扩展,当扩展是无法申请到足够的内存时会抛出OutOfMemoryError异常。
本地方法栈(Native Method Stacks):
  1. 为虚拟机使用到的Native方法服务;
  2. 同样也会抛出StackOverflow和OutOfMemoryError异常。
Java堆(Java Heap)
  1. Java堆是Java虚拟机所管理的内存中最大的一块;
  2. 所有线程共享一块内存区域,在虚拟机启动时创建;
  3. Java堆的唯一目的就是存放对象实例。Java规范中得描述是所有的对象实例以及数组都要在堆上分配;(但当前出现了一些栈上分配、标量替换的新方法。)
  4. 堆划分,内存回收的角度:新生代和老年代。 Eden空间、From Survivor空间、To Survivir空间。从内存分配的角度:线程共享的Java堆中可以划分出多个线程私有的分配缓冲区(Thread Local Allocation Buffer, TLAB);
  5. Java堆可以处于物理上不连续的内存空间中,只要逻辑上是连续的即可;
  6. 如果堆中没有内存完成实例分配,并且堆也无法再扩充时,将会抛出OutOfMemoryError异常。
方法区(Method Area)
  1. 各个线程共享的内存区域。别名叫Non-Heap;
  2. 用于存储已被虚拟机加载的类信息、常亮、静态变量、即时编译器编译后的代码数据
  3. Java虚拟机规范对这个区域的限制非常宽松,除了和Java堆一样不需要连续的内存和可以选择固定大小和可扩展外,还可以选择不实现垃圾收集。相对而言,垃圾收集在这个区域是比较少出现的。这个区域内存回收的目标主要是针对常量池的回收和对类型的卸载,一般来说对这个区域的回收成绩比较难以令人满意。
  4. 当方法区无法满足内存分配需求时,将抛出OutOfMemoryError异常。
运行时常量池(Runtime Constant Pool)
  1. 方法区的一部分
  2. Class文件中除了有类的版本、字段、方法、接口等描述信息,还有一项信息是常量池(Constant Pool Table),用于存放编译器生成的各种字面量和符号引用。
  3. Java虚拟机规范未对运行时常量池做任何细节要求,不同的提供商实现的虚拟机可以按照自己的需求来实现这个内存区域
  4. Java语言并不要求常量一定只能在编译期产生,也就是并非预置入Class文件中常量池的内容才能进入方法区运行时常量池,运行期间也可能将新的常量放入池中。如使用String类的intern()方法。
  5. 当常量池无法再申请到内存时会抛出OutOfMemoryError异常。
直接内存(Direct Memory)
  1. 并不是虚拟机运行时数据区的一部分,也不是Java虚拟机规范中定义的内存区域。
  2. NIO类,引入了一种基于通道(Channel)与缓冲区(Buffer)的I/O方式,可以使用Native函数直接分配堆外内存,然后通过一个存储在Java堆里面的DirectByteBuffer对象作为这块内存的引用进行操作。这样可以避免在Java堆和Native堆中来回复制数据,提升性能。
  3. 本机直接内存的分配不会受到Java堆大小的限制。但会受到本机总内存大小的限制。各个区域的总和大于物理内存限制时,会导致动态扩展时出现OutOfMemoryError异常。
2014/12/17 posted in  java

java activiti 实例工程maven实现

特殊依赖

activiti-common-rest 工程

maven-restlet
Public online Restlet repository
http://maven.restlet.org

activiti-explorer 工程

vaadin-addons
http://maven.vaadin.com/vaadin-addons

activiti-modeler 工程

maven-restlet
Public online Restlet repository
http://maven.restlet.org

activiti-mule 工程

mulesoft-releases
MuleSoft Releases Repository
http://repository.mulesoft.org/releases/
default

activiti-rest 工程

maven-restlet
Public online Restlet repository
http://maven.restlet.org

activiti-webapp-explorer2 工程

vaadin-addons
http://maven.vaadin.com/vaadin-addons


2014/10/19 posted in  java

mac环境下安装eclipse以及相关插件

导读

在mac下得eclipse版本和windows下的eclipse还是有许多区别的。一些插件的依赖不一样。
之前使用kapler版本,下载maven插件之后,用起来总是会报各种奇奇怪怪的问题,今天有空下了一个juno版本,问题就解决了,也在这里记录一下相应的配置。

下载安装eclipse

下载地址http://www.eclipse.org/downloads/

最好选择javaEE版本,否则建不了web工程,选择Eclipse IDE for Java EE Developers。

安装完成后本地解压就可以了

安装maven插件

安装maven插件
Eclipse -> Help -> Install New Software
Add a new software site:
http://download.eclipse.org/technology/m2e/releases

通过这个链接安装的是maven eclipse插件的1.5版本。 可以展开版本选择需要的版本安装。
在mac下的eclipse直接通过这种方法安装,会报一个缺少slf4j依赖的错,用下面方法可以解决。

安装slf4j-api 插件

安装maven插件的时候会报一个依赖slf4j的依赖,下载方法为

Eclipse -> Help -> Install New Software
Add a new software site:
Name: slf4j
Url: http://www.fuin.org/p2-repository/
Expand "Maven osgi-bundles" and select "slf4j-api"
Click "Next" and follow the installation.
直接安装maven 1.3 版本

如果安装1.5版本一直出问题,可以选择安装maven的1.3版本,在mac的eclipse下就补需要别的插件补丁了。

Eclipse -> Help -> Install New Software
Add a new software site:
http://download.eclipse.org/technology/m2e/releases/1.3
配置maven

具体配置方法参考这里

2014/10/19 posted in  java