Feeling

    三人行,必有我师焉

   ::  :: 新随笔 :: 联系 ::  :: 管理 ::
  185 随笔 :: 0 文章 :: 392 评论 :: 0 Trackbacks

2012年11月10日 #

     摘要: 很多网上下载的PDF文件都包含各种形式的水印,本文主要阐述如何使用易转换一键删除PDF文件中的各种图片水印和文字水印  阅读全文
posted @ 2021-03-09 20:29 三人行,必有我师焉 阅读(402) | 评论 (0)编辑 收藏

Spark源代码下载地址: http://spark.apache.org/downloads.html

下载后,直接用 Scala IDE 通过已存在的项目导入到Eclipse workspace中去,然后Eclipse会自动进行编译。第一次编译会报很多错误,不过总的来说,导致编译错误的源头有三个:
1、Scala编译器版本错误
2、Eclipse Maven插件不能自动识别spark project的一些pom,报Plugin execution not covered by lifecycle configuration异常
3、一些项目,maven会自动生成scala和java文件,但是这些自动生成的代码文件没有配置在eclipse项目的classpath里。

针对第一种错误,比较简单,对于每个scala项目,右键属性选择spark对应的scala编译器版本。



当然spark代码里的项目有几十个,只能手工一个个设置了,比较傻,没办法,还不停的弹出对话框,不停地回车吧。

编译的难点主要在第二种错误上,比如spark-sql项目的pom, 里面有个build-helper-maven-plugin,它下面的execution,eclipse maven插件无法识别,报Plugin execution not covered by lifecycle configuration异常,解决方案参见 https://www.eclipse.org/m2e/documentation/m2e-execution-not-covered.html,先使用 Eclipse quick-fix选项自动修复,忽略此 maven goal,Eclipse 会为 pom.xml自动添加一段xml代码,包含在 pluginManagement section中,里面有一段 <action><ignore/></action>,此处手动修改成
<action>
    <execute>
        <runOnIncremental>false</runOnIncremental>
    </execute>
</action>
然后右键 maven update project 就OK了。

一共有5个project需要修改pom,如图


修改pom后重新编译,依旧会报一些错误,这些错误都是由于maven自动生成的java和scala代码没有添加到classpath里导致的编译错误,只需要手工添加一下即可,需要手工添加项目有 spark-streaming-flume-sink 的 src_managed\main\compiled_avro 目录 和 spark-sql 项目的 test\gen-java 目录。

全部编译好以后的截图:


修改完以后,Spark代码全部编译下来大概耗时25分钟左右(CPU 双核 I7 4600)

原文地址:http://www.blogjava.net/cnfree/archive/2016/11/08/431965.html
posted @ 2016-11-08 13:12 三人行,必有我师焉 阅读(2236) | 评论 (0)编辑 收藏

  Spark简介

  Spark是整个BDAS的核心组件,是一个大数据分布式编程框架,不仅实现了MapReduce的算子map 函数和reduce函数及计算模型,还提供更为丰富的算子,如filter、join、groupByKey等。是一个用来实现快速而同用的集群计算的平台。

  Spark将分布式数据抽象为弹性分布式数据集(RDD),实现了应用任务调度、RPC、序列化和压缩,并为运行在其上的上层组件提供API。其底层采用Scala这种函数式语言书写而成,并且所提供的API深度借鉴Scala函数式的编程思想,提供与Scala类似的编程接口

  Sparkon Yarn

  

  从用户提交作业到作业运行结束整个运行期间的过程分析。

  一、客户端进行操作

  1. 根据yarnConf来初始化yarnClient,并启动yarnClient

  2. 创建客户端Application,并获取Application的ID,进一步判断集群中的资源是否满足executor和ApplicationMaster申请的资源,如果不满足则抛出IllegalArgumentException;

  3. 设置资源、环境变量:其中包括了设置Application的Staging目录、准备本地资源(jar文件、log4j.properties)、设置Application其中的环境变量、创建Container启动的Context等;

  4. 设置Application提交的Context,包括设置应用的名字、队列、AM的申请的Container、标记该作业的类型为Spark;

  5. 申请Memory,并最终通过yarnClient.submitApplication向ResourceManager提交该Application。

  当作业提交到YARN上之后,客户端就没事了,甚至在终端关掉那个进程也没事,因为整个作业运行在YARN集群上进行,运行的结果将会保存到HDFS或者日志中。

  二、提交到YARN集群,YARN操作

  1. 运行ApplicationMaster的run方法;

  2. 设置好相关的环境变量。

  3. 创建amClient,并启动;

  4. 在Spark UI启动之前设置Spark UI的AmIpFilter;

  5. 在startUserClass函数专门启动了一个线程(名称为Driver的线程)来启动用户提交的Application,也就是启动了Driver。在Driver中将会初始化SparkContext;

  6. 等待SparkContext初始化完成,最多等待spark.yarn.applicationMaster.waitTries次数(默认为10),如果等待了的次数超过了配置的,程序将会退出;否则用SparkContext初始化yarnAllocator;

  7. 当SparkContext、Driver初始化完成的时候,通过amClient向ResourceManager注册ApplicationMaster

  8. 分配并启动Executeors。在启动Executeors之前,先要通过yarnAllocator获取到numExecutors个Container,然后在Container中启动Executeors。

      那么这个Application将失败,将Application Status标明为FAILED,并将关闭SparkContext。其实,启动Executeors是通过ExecutorRunnable实现的,而ExecutorRunnable内部是启动CoarseGrainedExecutorBackend的。

  9. 最后,Task将在CoarseGrainedExecutorBackend里面运行,然后运行状况会通过Akka通知CoarseGrainedScheduler,直到作业运行完成。

  Spark节点的概念

  一、Spark驱动器是执行程序中的main()方法的进程。它执行用户编写的用来创建SparkContext(初始化)、创建RDD,以及运行RDD的转化操作和行动操作的代码。

  驱动器节点driver的职责:

  1. 把用户程序转为任务task(driver)

      Spark驱动器程序负责把用户程序转化为多个物理执行单元,这些单元也被称之为任务task(详解见备注)

  2. 为执行器节点调度任务(executor)

      有了物理计划之后,Spark驱动器在各个执行器节点进程间协调任务的调度。Spark驱动器程序会根据当前的执行器节点,把所有任务基于数据所在位置分配给合适的执行器进程。当执行任务时,执行器进程会把缓存的数据存储起来,而驱动器进程同样会跟踪这些缓存数据的位置,并利用这些位置信息来调度以后的任务,以尽量减少数据的网络传输。(就是所谓的移动计算,而不移动数据)。

  二、执行器节点

  作用:

  1. 负责运行组成Spark应用的任务,并将结果返回给驱动器进程;

  2. 通过自身的块管理器(blockManager)为用户程序中要求缓存的RDD提供内存式存储。RDD是直接缓存在执行器进程内的,因此任务可以在运行时充分利用缓存数据加快运算。

  驱动器的职责:

  所有的Spark程序都遵循同样的结构:程序从输入数据创建一系列RDD,再使用转化操作派生成新的RDD,最后使用行动操作手机或存储结果RDD,Spark程序其实是隐式地创建出了一个由操作组成的逻辑上的有向无环图DAG。当驱动器程序执行时,它会把这个逻辑图转为物理执行计划。

  这样 Spark就把逻辑计划转为一系列步骤(stage),而每个步骤又由多个任务组成。这些任务会被打包送到集群中。

  Spark初始化

  1. 每个Spark应用都由一个驱动器程序来发起集群上的各种并行操作。驱动器程序包含应用的main函数,并且定义了集群上的分布式数据集,以及对该分布式数据集应用了相关操作。

  2. 驱动器程序通过一个SparkContext对象来访问spark,这个对象代表对计算集群的一个连接。(比如在sparkshell启动时已经自动创建了一个SparkContext对象,是一个叫做SC的变量。(下图,查看变量sc)

      

  3. 一旦创建了sparkContext,就可以用它来创建RDD。比如调用sc.textFile()来创建一个代表文本中各行文本的RDD。(比如vallinesRDD = sc.textFile(“yangsy.text”),val spark = linesRDD.filter(line=>line.contains(“spark”),spark.count())

      执行这些操作,驱动器程序一般要管理多个执行器,就是我们所说的executor节点。

  4. 在初始化SparkContext的同时,加载sparkConf对象来加载集群的配置,从而创建sparkContext对象。

      从源码中可以看到,在启动thriftserver时,调用了spark- daemon.sh文件,该文件源码如左图,加载spark_home下的conf中的文件。

      

      (在执行后台代码时,需要首先创建conf对象,加载相应参数, val sparkConf = newSparkConf().setMaster("local").setAppName("cocapp").set("spark.executor.memory","1g"), val sc: SparkContext = new SparkContext(sparkConf))

  RDD工作原理:

  RDD(Resilient DistributedDatasets)[1] ,弹性分布式数据集,是分布式内存的一个抽象概念,RDD提供了一种高度受限的共享内存模型,即RDD是只读的记录分区的集合,只能通过在其他RDD执行确定的转换操作(如map、join和group by)而创建,然而这些限制使得实现容错的开销很低。对开发者而言,RDD可以看作是Spark的一个对象,它本身运行于内存中,如读文件是一个RDD,对文件计算是一个RDD,结果集也是一个RDD ,不同的分片、数据之间的依赖、key-value类型的map数据都可以看做RDD。

  主要分为三部分:创建RDD对象,DAG调度器创建执行计划,Task调度器分配任务并调度Worker开始运行。

  SparkContext(RDD相关操作)→通过(提交作业)→(遍历RDD拆分stage→生成作业)DAGScheduler→通过(提交任务集)→任务调度管理(TaskScheduler)→通过(按照资源获取任务)→任务调度管理(TaskSetManager)

  Transformation返回值还是一个RDD。它使用了链式调用的设计模式,对一个RDD进行计算后,变换成另外一个RDD,然后这个RDD又可以进行另外一次转换。这个过程是分布式的。

  Action返回值不是一个RDD。它要么是一个Scala的普通集合,要么是一个值,要么是空,最终或返回到Driver程序,或把RDD写入到文件系统中

  转换(Transformations)(如:map, filter, groupBy, join等),Transformations操作是Lazy的,也就是说从一个RDD转换生成另一个RDD的操作不是马上执行,Spark在遇到Transformations操作时只会记录需要这样的操作,并不会去执行,需要等到有Actions操作的时候才会真正启动计算过程进行计算。

  操作(Actions)(如:count, collect, save等),Actions操作会返回结果或把RDD数据写到存储系统中。Actions是触发Spark启动计算的动因。

  它们本质区别是:Transformation返回值还是一个RDD。它使用了链式调用的设计模式,对一个RDD进行计算后,变换成另外一个RDD,然后这个RDD又可以进行另外一次转换。这个过程是分布式的。Action返回值不是一个RDD。它要么是一个Scala的普通集合,要么是一个值,要么是空,最终或返回到Driver程序,或把RDD写入到文件系统中。关于这两个动作,在Spark开发指南中会有就进一步的详细介绍,它们是基于Spark开发的核心。

  RDD基础

  1. Spark中的RDD就是一个不可变的分布式对象集合。每个RDD都被分为多个分区,这些分区运行在集群的不同节点上。创建RDD的方法有两种:一种是读取一个外部数据集;一种是在群东程序里分发驱动器程序中的对象集合,不如刚才的示例,读取文本文件作为一个字符串的RDD的示例。

  2. 创建出来后,RDD支持两种类型的操作:转化操作和行动操作

      转化操作会由一个RDD生成一个新的RDD。(比如刚才的根据谓词筛选)

      行动操作会对RDD计算出一个结果,并把结果返回到驱动器程序中,或把结果存储到外部存储系统(比如HDFS)中。比如first()操作就是一个行动操作,会返回RDD的第一个元素。

      注:转化操作与行动操作的区别在于Spark计算RDD的方式不同。虽然你可以在任何时候定义一个新的RDD,但Spark只会惰性计算这些RDD。它们只有第一个在一个行动操作中用到时,才会真正的计算。之所以这样设计,是因为比如刚才调用sc.textFile(...)时就把文件中的所有行都读取并存储起来,就会消耗很多存储空间,而我们马上又要筛选掉其中的很多数据。

      这里还需要注意的一点是,spark会在你每次对它们进行行动操作时重新计算。如果想在多个行动操作中重用同一个RDD,那么可以使用RDD.persist()或RDD.collect()让Spark把这个RDD缓存下来。(可以是内存,也可以是磁盘)

  3. Spark会使用谱系图来记录这些不同RDD之间的依赖关系,Spark需要用这些信息来按需计算每个RDD,也可以依靠谱系图在持久化的RDD丢失部分数据时用来恢复所丢失的数据。(如下图,过滤errorsRDD与warningsRDD,最终调用union()函数)

      

  RDD计算方式

  

  RDD的宽窄依赖

  

  窄依赖 (narrowdependencies) 和宽依赖 (widedependencies) 。窄依赖是指 父 RDD 的每个分区都只被子 RDD 的一个分区所使用 。相应的,那么宽依赖就是指父 RDD 的分区被多个子 RDD 的分区所依赖。例如, map 就是一种窄依赖,而 join 则会导致宽依赖

  这种划分有两个用处。首先,窄依赖支持在一个结点上管道化执行。例如基于一对一的关系,可以在 filter 之后执行 map 。其次,窄依赖支持更高效的故障还原。因为对于窄依赖,只有丢失的父 RDD 的分区需要重新计算。而对于宽依赖,一个结点的故障可能导致来自所有父 RDD 的分区丢失,因此就需要完全重新执行。因此对于宽依赖,Spark 会在持有各个父分区的结点上,将中间数据持久化来简化故障还原,就像 MapReduce 会持久化 map 的输出一样。

  SparkExample

  

  步骤 1 :创建 RDD 。上面的例子除去最后一个 collect 是个动作,不会创建 RDD 之外,前面四个转换都会创建出新的 RDD 。因此第一步就是创建好所有 RDD( 内部的五项信息 ) 。

  步骤 2 :创建执行计划。Spark 会尽可能地管道化,并基于是否要重新组织数据来划分 阶段 (stage) ,例如本例中的 groupBy() 转换就会将整个执行计划划分成两阶段执行。最终会产生一个 DAG(directedacyclic graph ,有向无环图 ) 作为逻辑执行计划。

  步骤 3 :调度任务。 将各阶段划分成不同的 任务 (task) ,每个任务都是数据和计算的合体。在进行下一阶段前,当前阶段的所有任务都要执行完成。因为下一阶段的第一个转换一定是重新组织数据的,所以必须等当前阶段所有结果数据都计算出来了才能继续。

  假设本例中的 hdfs://names 下有四个文件块,那么 HadoopRDD 中 partitions 就会有四个分区对应这四个块数据,同时 preferedLocations 会指明这四个块的最佳位置。现在,就可以创建出四个任务,并调度到合适的集群结点上。

  Spark数据分区

  1. Spark的特性是对数据集在节点间的分区进行控制。在分布式系统中,通讯的代价是巨大的,控制数据分布以获得最少的网络传输可以极大地提升整体性能。Spark程序可以通过控制RDD分区方式来减少通讯的开销。

  2. Spark中所有的键值对RDD都可以进行分区。确保同一组的键出现在同一个节点上。比如,使用哈希分区将一个RDD分成了100个分区,此时键的哈希值对100取模的结果相同的记录会被放在一个节点上。

      (可使用partitionBy(newHashPartitioner(100)).persist()来构造100个分区)

  3. Spark中的许多操作都引入了将数据根据键跨界点进行混洗的过程。(比如:join(),leftOuterJoin(),groupByKey(),reducebyKey()等)对于像reduceByKey()这样只作用于单个RDD的操作,运行在未分区的RDD上的时候会导致每个键的所有对应值都在每台机器上进行本地计算。

  SparkSQL的shuffle过程

  

  Spark SQL的核心是把已有的RDD,带上Schema信息,然后注册成类似sql里的”Table”,对其进行sql查询。这里面主要分两部分,一是生成SchemaRD,二是执行查询。

  如果是spark-hive项目,那么读取metadata信息作为Schema、读取hdfs上数据的过程交给Hive完成,然后根据这俩部分生成SchemaRDD,在HiveContext下进行hql()查询。

  SparkSQL结构化数据

  1. 首先说一下ApacheHive,Hive可以在HDFS内或者在其他存储系统上存储多种格式的表。SparkSQL可以读取Hive支持的任何表。要把Spark SQL连接已有的hive上,需要提供Hive的配置文件。hive-site.xml文件复制到spark的conf文件夹下。再创建出HiveContext对象(sparksql的入口),然后就可以使用HQL来对表进行查询,并以由行足证的RDD的形式拿到返回的数据。

  2. 创建Hivecontext并查询数据

      importorg.apache.spark.sql.hive.HiveContext

      valhiveCtx = new org.apache.spark.sql.hive.HiveContext(sc)

      valrows = hiveCtx.sql(“SELECT name,age FROM users”)

      valfitstRow – rows.first()

      println(fitstRow.getSgtring(0)) //字段0是name字段

  3. 通过jdbc连接外部数据源更新与加载

      Class.forName("com.mysql.jdbc.Driver")

      val conn =DriverManager.getConnection(mySQLUrl)

      val stat1 =conn.createStatement()

      stat1.execute("UPDATE CI_LABEL_INFO set DATA_STATUS_ID = 2 , DATA_DATE ='" + dataDate +"' where LABEL_ID in ("+allCreatedLabels.mkString(",")+")")

      stat1.close()

      //加载外部数据源数据到内存

      valDIM_COC_INDEX_MODEL_TABLE_CONF =sqlContext.jdbc(mySQLUrl,"DIM_COC_INDEX_MODEL_TABLE_CONF").cache()

      val targets =DIM_COC_INDEX_MODEL_TABLE_CONF.filter("TABLE_DATA_CYCLE ="+TABLE_DATA_CYCLE).collect

  SparkSQL解析

  

  首先说下传统数据库的解析,传统数据库的解析过程是按Rusult、Data Source、Operation的次序来解析的。传统数据库先将读入的SQL语句进行解析,分辨出SQL语句中哪些词是关键字(如select,from,where),哪些是表达式,哪些是Projection,哪些是Data Source等等。进一步判断SQL语句是否规范,不规范就报错,规范则按照下一步过程绑定(Bind)。过程绑定是将SQL语句和数据库的数据字典(列,表,视图等)进行绑定,如果相关的Projection、Data Source等都存在,就表示这个SQL语句是可以执行的。在执行过程中,有时候甚至不需要读取物理表就可以返回结果,比如重新运行刚运行过的SQL语句,直接从数据库的缓冲池中获取返回结果。在数据库解析的过程中SQL语句时,将会把SQL语句转化成一个树形结构来进行处理,会形成一个或含有多个节点(TreeNode)的Tree,然后再后续的处理政对该Tree进行一系列的操作。

  Spark SQL对SQL语句的处理和关系数据库对SQL语句的解析采用了类似的方法,首先会将SQL语句进行解析,然后形成一个Tree,后续如绑定、优化等处理过程都是对Tree的操作,而操作方法是采用Rule,通过模式匹配,对不同类型的节点采用不同的操作。SparkSQL有两个分支,sqlContext和hiveContext。sqlContext现在只支持SQL语法解析器(Catalyst),hiveContext支持SQL语法和HiveContext语法解析器。

原文地址:http://mt.sohu.com/20160522/n450849016.shtml

posted @ 2016-09-08 13:11 三人行,必有我师焉 阅读(248) | 评论 (0)编辑 收藏

spark中有partition的概念(和slice是同一个概念,在spark1.2中官网已经做出了说明),一般每个partition对应一个task。在我的测试过程中,如果没有设置spark.default.parallelism参数,spark计算出来的partition非常巨大,与我的cores非常不搭。我在两台机器上(8cores *2 +6g * 2)上,spark计算出来的partition达到2.8万个,也就是2.9万个tasks,每个task完成时间都是几毫秒或者零点几毫秒,执行起来非常缓慢。在我尝试设置了 spark.default.parallelism 后,任务数减少到10,执行一次计算过程从minute降到20second。

参数可以通过spark_home/conf/spark-default.conf配置文件设置。

eg.

spark.master  spark://master:7077 

spark.default.parallelism  10 

spark.driver.memory  2g 

spark.serializer  org.apache.spark.serializer.KryoSerializer 

spark.sql.shuffle.partitions  50

 

下面是官网的相关描述:

from:http://spark.apache.org/docs/latest/configuration.html

Property NameDefaultMeaning
spark.default.parallelism For distributed shuffle operations like reduceByKey and join, the largest number of partitions in a parent RDD. For operations likeparallelize with no parent RDDs, it depends on the cluster manager:
  • Local mode: number of cores on the local machine
  • Mesos fine grained mode: 8
  • Others: total number of cores on all executor nodes or 2, whichever is larger
Default number of partitions in RDDs returned by transformations like joinreduceByKey, and parallelize when not set by user.

from:http://spark.apache.org/docs/latest/tuning.html

Level of Parallelism

Clusters will not be fully utilized unless you set the level of parallelism for each operation high enough. Spark automatically sets the number of “map” tasks to run on each file according to its size (though you can control it through optional parameters to SparkContext.textFile, etc), and for distributed “reduce” operations, such as groupByKey and reduceByKey, it uses the largest parent RDD’s number of partitions. You can pass the level of parallelism as a second argument (see the spark.PairRDDFunctions documentation), or set the config propertyspark.default.parallelism to change the default. In general, we recommend 2-3 tasks per CPU core in your cluster.


原文地址:http://www.cnblogs.com/wrencai/p/4231966.html

posted @ 2016-09-08 13:07 三人行,必有我师焉 阅读(2195) | 评论 (0)编辑 收藏

Eclipse Class Decompiler是一款Eclipse插件,整合了多种反编译器,和Eclipse Class Viewer无缝集成,能够很方便的使用插件查看类库源码,进行Debug调试。
同时还提供了在线自动查找源代码,查看Class二进制字节码的功能。
 

Eclipse Class Decompiler对JDK的最低要求为JDK1.6, 能反编译和debug各版本的Class文件,支持JDK8的Lambda语法,同时支持中文等非Ascii码字符集的解析,支持Eclipse 3.6及以上所有版本的Eclipse。

本插件支持Windows,Linux,Macosx 32位及64位操作系统。

Github项目地址为:https://github.com/cnfree/Eclipse-Class-Decompiler

请通过以下地址选择一个可用的源在线安装:

http://cnfree.github.io/Eclipse-Class-Decompiler/update
http://raw.githubusercontent.com/cnfree/eclipse/master/decompiler/update/
http://www.cpupk.com/decompiler/update/

离线包下载地址:
https://github.com/cnfree/Eclipse-Class-Decompiler/releases/download/v2.10.0/eclipse-class-decompiler-update_v2.10.0.zip

 
插件使用说明:

下图为Eclipse Class Decompiler的首选项页面,可以选择缺省的反编译器工具,并进行反编译器的基本设置。缺省的反编译工具为JD-Core,JD-Core更为先进一些,支持泛型、Enum、注解等JDK1.5以后才有的新语法。

首选项配置选项:
1.重用缓存代码:只会反编译一次,以后每次打开该类文件,都显示的是缓存的反编译代码。
2.忽略已存在的源代码:若未选中,则查看Class文件是否已绑定了Java源代码,如果已绑定,则显示Java源代码,如果未绑定,则反编译Class文件。若选中此项,则忽略已绑定的Java源代码,显示反编译结果。
3.显示反编译器报告:显示反编译器反编译后生成的数据报告及异常信息。
4.使用Eclipse代码格式化工具:使用Eclipse格式化工具对反编译结果重新格式化排版,反编译整个Jar包时,此操作会消耗一些时间。
5.使用Eclipse成员排序:使用Eclipse成员排序对反编译结果重新格式化排版,反编译整个Jar包时,此操作会消耗大量时间。
6.以注释方式输出原始行号信息:如果Class文件包含原始行号信息,则会将行号信息以注释的方式打印到反编译结果中。
7.根据行号对齐源代码以便于调试:若选中该项,插件会采用AST工具分析反编译结果,并根据行号信息调整代码顺序,以便于Debug过程中的单步跟踪调试。
8.设置类反编译查看器作为缺省的类文件编辑器:默认为选中,将忽略Eclipse自带的Class Viewer,每次Eclipse启动后,默认使用本插件提供的类查看器打开Class文件。



插件提供了系统菜单,工具栏,当打开了插件提供的类反编译查看器后,会激活菜单和工具栏选项,可以方便的进行首选项配置,切换反编译工具重新反编译,以及导出反编译结果。






类反编译查看器右键菜单包含了Eclipse自带类查看器右键菜单的全部选项,并增加了一个“导出反编译源代码”菜单项。



打开项目路径下的Class文件,如果设置类反编译查看器为缺省的查看器,直接双击Class文件即可,如果没有设置为缺省查看器,可以使用右键菜单进行查看。




同时插件也支持直接将外部的Class文件拖拽到Eclipse编辑器中进行反编译。


Eclipse Class Decompiler插件也提供了反编译整个Jar文件或者Java包的反编译。该操作支持Package Explorer对包显示布局的操作,如果是平铺模式布局,则导出的源代码不包含子包,如果是层级模式布局,则导出选中的包及其所有的子包。




Debug调试:可以在首选项选中对齐行号进行单步跟踪调试,和普通的包含源代码时的调试操作完全一致,同样的也可以设置断点进行跟踪。当透视图为Debug时,插件自动生成行号并进行对齐方便调试代码,无需进行任何设置。


博文地址:http://www.blogjava.net/cnfree/archive/2012/10/30/390457.html
posted @ 2016-05-13 14:23 三人行,必有我师焉 阅读(1302) | 评论 (5)编辑 收藏

Java应用定制工厂(以下简称为JCB,Java Customization Builder)是一个针对Java轻量级桌面应用进行精简优化的小工具,使用它可以精简你的jar包,并自动生成一个精简的JRE,也可以使用它生成一个Exe启动引导程序,并且能够对你的Java应用自动做Pack200和Unpack200处理。使用本工具定制的Java桌面应用通常不会超过10M(包含JRE),SWT客户端程序相对于Swing客户端程序更小,一般不会超过5M。

JCB是一个Java应用,所以目标机器上必须安装1.5以上版本的JDK用以启动JCB,但是JCB可以用来精简1.4版的JRE,并且JRE1.4精简后的体积远小于1.5以上的版本。

1.新建JCB项目
精简JRE的步骤比较繁琐,有可能精简失败,为了不重复之前的步骤,JCB提供一个项目文件用来保存精简配置信息,扩展名为jcprj。这里我们创建一个项目,名为JCB


Wizard需要输入一个工程名和指定工程位置,至于下面的应用程序位置和定制JRE位置由JCB自动指定,这儿显示出来仅供参考。

此时最好Ctrl+S保存一下项目,否则退出后你之前的配置信息会全部丢失,因为你并没有制定一个可用的项目配置文件。

2. 配置JCB项目


首先指定项目需要的jar文件,然后依次选择项目的main class,启动路径默认为空,一般来说无需指定。然后设定应用程序参数和虚拟机参数。最后选定需要精简的JRE,JCB当前支持1.4-1.7版本的JRE,未来可能会支持更高版本的JRE。

右下角有2个单选按钮:全部重新运行和增量运行。全部重新运行就会放弃之前的运行结果,增量运行就是会保留以前的运行结果。

然后点击“以Verbose模式运行”按钮。Verbose模式运行Java程序,会显示JVM加载的全部类信息,JCB需要这些类信息进行JRE的精简,所以请尽可能的把应用所有的功能尽可能的跑一遍,跑的越全面,导致精简出错的可能性就越低。



Verbose运行结果,这个页面的显示信息仅供参考,无实际用处。

3. 分析项目的类依赖项


分析类依赖模式有2个选项:重新完全分析和增量分析。完全分析会花费较多的时间。当使用verbose模式增量运行后,可以使用增量模式分析类依赖项,这样可以节约大量的时间。类依赖分析会反编译所有运行的类,分析类引用关系,但是无法获取Class.forName这类动态类加载信息,所以需要Verbose模式运行的尽量全面,以避免这些动态加载的类的缺失。

为什么需要分析类依赖关系呢?因为不同的操作系统,不同的硬件配置,JRE可能会采取策略模式加载不同的类,或者一些异常,Verbose模式一般不会加载,这样换个硬件环境,仅仅使用Verbose模式的类可能会导致ClassNotFound这样的异常,导致Java程序崩溃。


4. 精简JRE


精简JRE有两种模式:使用Verbose运行结果和使用类依赖分析结果。前者只包含Verbose分析出来的类,精简出来的JRE包很小,但是基本不具备跨平台性。所以一般来说推荐选择后者。

如果你的程序包含Swing的客户端,并且比较复杂的话,最好选中包含Swing选项。因为Swing的设计完全是动态化的加载,全部使用Class.forName方式,类依赖分析对Swing是无效的。当然选中该选项后,JRE的体积会增加许多。比较好的解决方案,是使用SWT替代Swing进行开发,或者尽量把你的程序跑全面,包括各种异常界面都跑出来。

右下角有两个按钮,是用来自定义类和资源文件的,比如移除JAR包的MD5文件或者无用的文件。或者测试运行发现ClassNotFound异常,手动把缺少的类加进去,然后JCB会自动运行增量类依赖分析加载所有可能需要的类。

选择左上角的“精简Jar包”按钮,就可以对JRE进行精简了,精简完毕后可以点击“查看精简结果”按钮进行查看。

5.定制JRE


上图显示了JRE精简结果,JCB会自动分析所有的Class,生成精简版JRE,包括需要的JAR,DLL和资源文件。一般来说精简出来的JRE,普通功能都能正确完成,但是不排除有些功能不能正常使用,比如缺少某个资源文件或者DLL,需要手工添加。

为了保证精简的正确性,你需要进行运行测试,这一步是必须的,而且最好和Verbose运行模式一样,把所有的功能都跑一遍,确认精简无误。



如果测试运行有误的话,请根据运行错误报告进行分析,如果缺少类,请使用Verbose模式重新运行相应的功能,或者在步骤四手工添加需要的类,然后重新生成依赖的JRE。如果缺少相关的DLL或者资源文件,也请手工添加,并且取消步骤四的“清理工作区选项”,否则每次精简JRE都需要重新手工添加。

到此为止,精简JRE部分就算全部完成了,你最好使用Ctrl+S保存一下结果,以避免下次重做项目。

JCB项目下载地址:http://www.sourceforge.net/projects/jcb
posted @ 2013-03-03 17:25 三人行,必有我师焉 阅读(5207) | 评论 (13)编辑 收藏

1. 40亿个无符号整数,找出一个不在这40亿个整数中的数。可以换个方向思考, 99个小于100的数,找出一个不在这99个数中的小于100的数。
首先把这99个数分为10组,按高位为0-9分,然后计算每组的数量,数量最少的那个肯定就是缺失的那个,然后递归……找最少的那个,组合起来的数肯定是缺失的。答案是按位运算找,和这个类似。

2. 43亿个无符号整数,找出一个重复的整数。也就是101个小于100的数,找出重复的那个数来。
首先把这99个数分为10组,按高位为0-9分,然后计算每组的数量,数量最多的那组,肯定有重复的,一次类推找第二位……
posted @ 2012-11-24 22:21 三人行,必有我师焉 阅读(395) | 评论 (0)编辑 收藏

When a object creates a new object, please use the dependency.

When a object just uses a object, please use the association. 
posted @ 2012-11-19 13:16 三人行,必有我师焉 阅读(279) | 评论 (0)编辑 收藏

comparator 

Decorator Pattern and Adapter Pattern have the same alias name: wrapper. But they face different aspects. Decorator pattern changes the object function, but the adapter pattern changes the interface.

The typical decorator pattern is the java OutputStream, you can use the BufferedOutputStream to wrap it, then get the extra function.
The typical adapter pattern in the BIRT is the ElementAdapter, it can convert any object to an other object.

Decorator pattern must extend the class which you want to wrap, but the adapter class must implements the interface using by the client.


FlyWeight pattern extracts the same part of some different objects, and the part doesn't be changed when these objects changed. String class uses the FlyWeight pattern, jface 
ImageRegistry also uses it. 
FlyWeight can have a interface to get external data, and change the external data's status, but FlyWeight internal status shouldn't be changed.

The Collections.sort() method implementation contains template method design pattern and strategy design pattern, but it doesn't contain the visitor design pattern. The Collections.sort() method uses the merge sort algorithm, you can't change it, but you can change the comparator logic, it's one step of the sort algorithm. So it's a template method pattern, but not a classic implementation, it uses the callback method to implement the pattern, but not extending the parent template class. The comparator class use the strategy design pattern, it not a visitor pattern, visitor pattern have a accept method to operate the element to deal some logic. 



posted @ 2012-11-14 00:22 三人行,必有我师焉 阅读(319) | 评论 (0)编辑 收藏

1 归并排序(MergeSort)

归并排序最差运行时间是O(nlogn),它是利用递归设计程序的典型例子。

归并排序的最基础的操作就是合并两个已经排好序的序列。

假设我们有一个没有排好序的序列,那么首先我们使用分割的办法将这个序列分割成一个一个已经排好序的子序列。然后再利用归并的方法将一个个的子序列合并成排序好的序列。分割和归并的过程可以看下面的图例。



从上图可以看出,我们首先把一个未排序的序列从中间分割成2部分,再把2部分分成4部分,依次分割下去,直到分割成一个一个的数据,再把这些数据两两归并到一起,使之有序,不停的归并,最后成为一个排好序的序列。

如何把两个已经排序好的子序列归并成一个排好序的序列呢?可以参看下面的方法。

假设我们有两个已经排序好的子序列。
序列A:1 23 34 65
序列B:2 13 14 87
那么可以按照下面的步骤将它们归并到一个序列中。

(1)首先设定一个新的数列C[8]。
(2)A[0]和B[0]比较,A[0] = 1,B[0] = 2,A[0] < B[0],那么C[0] = 1
(3)A[1]和B[0]比较,A[1] = 23,B[0] = 2,A[1] > B[0],那么C[1] = 2
(4)A[1]和B[1]比较,A[1] = 23,B[1] = 13,A[1] > B[1],那么C[2] = 13
(5)A[1]和B[2]比较,A[1] = 23,B[2] = 14,A[1] > B[2],那么C[3] = 14
(6)A[1]和B[3]比较,A[1] = 23,B[3] = 87,A[1] < B[3],那么C[4] = 23
(7)A[2]和B[3]比较,A[2] = 34,B[3] = 87,A[2] < B[3],那么C[5] = 34
(8)A[3]和B[3]比较,A[3] = 65,B[3] = 87,A[3] < B[3],那么C[6] = 65
(9)最后将B[3]复制到C中,那么C[7] = 87。归并完成。

如果我们清楚了上面的分割和归并过程,那么我们就可以用递归的方法得到归并算法的实现。

    public class MergeSorter
    {
        
private static int[] myArray;
        
private static int arraySize;

        
public static void Sort( int[] a )
        {
            myArray 
= a;
            arraySize 
= myArray.Length;
            MergeSort();
        }

        
/// <summary>
        
/// 利用归并的方法排序数组,首先将序列分割
        
/// 然后将数列归并,这个算法需要双倍的存储空间
        
/// 时间是O(nlgn)
        
/// </summary>
        private static void MergeSort()
        {
            
int[] temp = new int[arraySize];
            MSort( temp, 
0, arraySize - 1);
        }

        
private static void MSort(int[] temp, int left, int right)
        {
            
int mid;

            
if (right > left)
            {
                mid 
= (right + left) / 2;
                MSort( temp, left, mid); 
//分割左边的序列
                MSort(temp, mid+1, right);//分割右边的序列
                Merge(temp, left, mid+1, right);//归并序列
            }
        }

        
private static void Merge( int[] temp, int left, int mid, int right)
        {
            
int i, left_end, num_elements, tmp_pos;

            left_end 
= mid - 1;
            tmp_pos 
= left;
            num_elements 
= right - left + 1;

            
while ((left <= left_end) && (mid <= right)) 
            {
                
if (myArray[left] <= myArray[mid]) //将左端序列归并到temp数组中
                {
                    temp[tmp_pos] 
= myArray[left];
                    tmp_pos 
= tmp_pos + 1;
                    left 
= left +1;
                }
                
else//将右端序列归并到temp数组中
                {
                    temp[tmp_pos] 
= myArray[mid];
                    tmp_pos 
= tmp_pos + 1;
                    mid 
= mid + 1;
                }
            }

            
while (left <= left_end) //拷贝左边剩余的数据到temp数组中
            {
                temp[tmp_pos] 
= myArray[left];
                left 
= left + 1;
                tmp_pos 
= tmp_pos + 1;
            }
            
while (mid <= right) //拷贝右边剩余的数据到temp数组中
            {
                temp[tmp_pos] 
= myArray[mid];
                mid 
= mid + 1;
                tmp_pos 
= tmp_pos + 1;
            }

            
for (i=0; i < num_elements; i++//将所有元素拷贝到原始数组中
            {
                myArray[right] 
= temp[right];
                right 
= right - 1;
            }
        }
    }


归并排序算法是一种O(nlogn)的算法。它的最差,平均,最好时间都是O(nlogn)。但是它需要额外的存储空间,这在某些内存紧张的机器上会受到限制。

归并算法是又分割和归并两部分组成的。对于分割部分,如果我们使用二分查找的话,时间是O(logn),在最后归并的时候,时间是O(n),所以总的时间是O(nlogn)。

2 堆排序(HeapSort)

堆排序属于百万俱乐部的成员。它特别适合超大数据量(百万条记录以上)的排序。因为它并不使用递归(因为超大数据量的递归可能会导致堆栈溢出),而且它的时间也是O(nlogn)。还有它并不需要大量的额外存储空间。

堆排序的思路是:

(1)将原始未排序的数据建成一个堆。
(2)建成堆以后,最大值在堆顶,也就是第0个元素,这时候将第零个元素和最后一个元素交换。
(3)这时候将从0到倒数第二个元素的所有数据当成一个新的序列,建一个新的堆,再次交换第一个和最后一个元素,依次类推,就可以将所有元素排序完毕。

建立堆的过程如下面的图所示:


堆排序的具体算法如下:

public class HeapSorter 
    {
        
private static int[] myArray;
        
private static int arraySize;

        
public static void Sort( int[] a )
        {
            myArray 
= a;
            arraySize 
= myArray.Length;
            HeapSort();
        }

        
private static void HeapSort()
        {
            BuildHeap();            
//将原始序列建成一个堆

            
while ( arraySize > 1 )
            {
                arraySize
--;
                Exchange ( 
0, arraySize );//将最大值放在数组的最后
                DownHeap ( 0 );  //将序列从0到n-1看成一个新的序列,重新建立堆
            } 
        }

        
private static void BuildHeap()
        {
            
for (int v=arraySize/2-1; v>=0; v--)
                DownHeap ( v );
        }

        
//利用向下遍历子节点建立堆
        private static void DownHeap( int v )
        {
            
int w = 2 * v + 1;                     // 节点w是节点v的第一个子节点

            
while (w < arraySize)
            {
                
if ( w+1 < arraySize )        // 如果节点v下面有第二个字节点
                    if ( myArray[w+1> myArray[w] ) 
                        w
++;                        // 将子节点w设置成节点v下面值最大的子节点

                 
// 节点v已经大于子节点w,有了堆的性质,那么返回
                if ( myArray[v] >= myArray[w] ) 
                    
return;   
                
                Exchange( v, w );     
// 如果不是,就交换节点v和节点w的值
                v = w;        
                w 
= 2 * v + 1;            // 继续向下找子节点
            }
        }

        
//交换数据
        private static void Exchange( int i, int j )
        {
            
int t = myArray[i];
            myArray[i] 
= myArray[j];
            myArray[j] 
= t;
        }
    }    


 

堆排序主要用于超大规模的数据的排序。因为它不需要额外的存储空间,也不需要大量的递归。

3 几种O(nlogn)算法的初步比较

我们可以从下表看到几种O(nlogn)算法的效率的区别。所有的数据都使用.Net的Random类产生,每种算法运行100次,时间的单位为毫秒。


500随机整数5000随机整数20000随机整数
合并排序0.31251.56257.03125
 Shell排序0.31251.256.875
堆排序0.468752.18756.71875
快速排序0.156250.6252.8125

从上表可以明显地看出,快速排序是最快的算法。这也就给了我们一个结论,对于一般的应用来说,我们总是选择快速排序作为我们的排序算法,当数据量非常大(百万数量级)我们可以使用堆排序,如果内存空间非常紧张,我们可以使用Shell排序。但是这意味着我们不得不损失速度。 

/******************************************************************************************
 *【Author】:flyingbread
 *【Date】:2007年2月2日
 *【Notice】:
 *1、本文为原创技术文章,首发博客园个人站点(http://flyingbread.cnblogs.com/),转载和引用请注明作者及出处。
 *2、本文必须全文转载和引用,任何组织和个人未授权不能修改任何内容,并且未授权不可用于商业。
 *3、本声明为文章一部分,转载和引用必须包括在原文中。
 ******************************************************************************************/
posted @ 2012-11-10 23:18 三人行,必有我师焉 阅读(595) | 评论 (2)编辑 收藏

1 快速排序(QuickSort)

快速排序是一个就地排序,分而治之,大规模递归的算法。从本质上来说,它是归并排序的就地版本。快速排序可以由下面四步组成。

(1) 如果不多于1个数据,直接返回。
(2) 一般选择序列最左边的值作为支点数据。
(3) 将序列分成2部分,一部分都大于支点数据,另外一部分都小于支点数据。
(4) 对两边利用递归排序数列。

快速排序比大部分排序算法都要快。尽管我们可以在某些特殊的情况下写出比快速排序快的算法,但是就通常情况而言,没有比它更快的了。快速排序是递归的,对于内存非常有限的机器来说,它不是一个好的选择。 

2 归并排序(MergeSort)

归并排序先分解要排序的序列,从1分成2,2分成4,依次分解,当分解到只有1个一组的时候,就可以排序这些分组,然后依次合并回原来的序列中,这样就可以排序所有数据。合并排序比堆排序稍微快一点,但是需要比堆排序多一倍的内存空间,因为它需要一个额外的数组。

3 堆排序(HeapSort)

堆排序适合于数据量非常大的场合(百万数据)。

堆排序不需要大量的递归或者多维的暂存数组。这对于数据量非常巨大的序列是合适的。比如超过数百万条记录,因为快速排序,归并排序都使用递归来设计算法,在数据量非常大的时候,可能会发生堆栈溢出错误。

堆排序会将所有的数据建成一个堆,最大的数据在堆顶,然后将堆顶数据和序列的最后一个数据交换。接下来再次重建堆,交换数据,依次下去,就可以排序所有的数据。

4 Shell排序(ShellSort)

Shell排序通过将数据分成不同的组,先对每一组进行排序,然后再对所有的元素进行一次插入排序,以减少数据交换和移动的次数。平均效率是O(nlogn)。其中分组的合理性会对算法产生重要的影响。现在多用D.E.Knuth的分组方法。

Shell排序比冒泡排序快5倍,比插入排序大致快2倍。Shell排序比起QuickSort,MergeSort,HeapSort慢很多。但是它相对比较简单,它适合于数据量在5000以下并且速度并不是特别重要的场合。它对于数据量较小的数列重复排序是非常好的。

5 插入排序(InsertSort)

插入排序通过把序列中的值插入一个已经排序好的序列中,直到该序列的结束。插入排序是对冒泡排序的改进。它比冒泡排序快2倍。一般不用在数据大于1000的场合下使用插入排序,或者重复排序超过200数据项的序列。

6 冒泡排序(BubbleSort)

冒泡排序是最慢的排序算法。在实际运用中它是效率最低的算法。它通过一趟又一趟地比较数组中的每一个元素,使较大的数据下沉,较小的数据上升。它是O(n^2)的算法。

7 交换排序(ExchangeSort)和选择排序(SelectSort)

这两种排序方法都是交换方法的排序算法,效率都是 O(n2)。在实际应用中处于和冒泡排序基本相同的地位。它们只是排序算法发展的初级阶段,在实际中使用较少。

8 基数排序(RadixSort)

基数排序和通常的排序算法并不走同样的路线。它是一种比较新颖的算法,但是它只能用于整数的排序,如果我们要把同样的办法运用到浮点数上,我们必须了解浮点数的存储格式,并通过特殊的方式将浮点数映射到整数上,然后再映射回去,这是非常麻烦的事情,因此,它的使用同样也不多。而且,最重要的是,这样算法也需要较多的存储空间。

9 总结

下面是一个总的表格,大致总结了我们常见的所有的排序算法的特点。
排序法 平均时间最差情形稳定度额外空间备注
冒泡 O(n2)  O(n2) 稳定O(1)n小时较好
交换  O(n2)  O(n2)不稳定O(1)n小时较好
选择 O(n2) O(n2)不稳定O(1)n小时较好
插入 O(n2) O(n2)稳定O(1)大部分已排序时较好
基数O(logRB)O(logRB)稳定O(n)

B是真数(0-9),

R是基数(个十百)

ShellO(nlogn)O(ns) 1<2不稳定O(1)s是所选分组
快速O(nlogn)O(n2)不稳定O(nlogn)n大时较好
归并O(nlogn)O(nlogn)稳定O(1)n大时较好
O(nlogn)O(nlogn)不稳定O(1)n大时较好

posted @ 2012-11-10 22:30 三人行,必有我师焉 阅读(333) | 评论 (0)编辑 收藏

GitHub |  开源中国社区 |  maven仓库 |  文件格式转换