文档章节

设置JVM编码 如果JVM不设置编码JVM会取系统默认的编码

五大三粗
 五大三粗
发布于 2015/10/15 09:32
字数 1645
阅读 6756
收藏 5
点赞 1
评论 1

1、题:

最近在往 HBase 写中文的时候,发现 hbase 查出来的数据会有部分中文乱码了,而部分中文又是正常的,按理来说,一般的乱码问题要么全乱,要么不乱。考虑到出现中文的地方都是来源于 hdfs 上的一个配置文件,而这个配置文件可以确定是 utf-8 编码的,那排除了原始文件导致的乱码,想想 MR 代码里也没有转码的逻辑,也排除了代码的问题,那就只有一种可能:Hadoop 集群的系统环境是异构的,这里面可能涉及到 linux 、java 的环境变量、配置的问题。

2、排查:

(1)打印了整个集群的 echo $LANG、echo $LC_ALL 等linux系统变量,发现都是一致的,排除了 os 环境的问题。

(2)剩下的重点放在了 java 环境上,在代码里加上如下两句,打印每条记录的 ip 和 jvm 编码,然后看看乱码的记录是那台机器产生的,并且当时 jvm child 的编码情况:

?
1
2
3
java.net.InetAddress test = java.net.InetAddress.getByName("localhost");
put.add(Bytes.toBytes("cf"), Bytes.toBytes("ip"), Bytes.toBytes(test.getLocalHost().getHostAddress()));
put.add(Bytes.toBytes("cf"), Bytes.toBytes("ec"), Bytes.toBytes(System.getProperty("file.encoding")));
同时也直接    System.out.println 出相应的中文字段,看是写进 hbase 之前还是之后乱掉的。

跑了一份测试数据后,发现 hbase 里的 ip、jvm 编码是没有规律的,然后查看 syso 打印的 log 发现,在写 hbase 之前已经就已经乱码了,然后想想 hbase 里的数据乱码之所以没有规律是因为 map 后要 shuffle、reduce 才能到 hbase。PS:sysout本身无编码概念,类似 linux 下的 cat、head、more 等。

然后再次把 ip、jvm编码 统计代码放到 map 阶段输出,果真发现了规律,集群中有两台机器的 jvm 编码不一致,不是 utf-8 的:

到这里我们可以知道原因了:由于集群中两台机器的 jvm 参数(file.encoding)不一致导致了部分中文结果的乱码。

3、解决方案:

知道原因了,那就看如何解决了,目的就是要改变 file.encoding 的值 。

(1)永久方案:

由于这个参数是 jvm 的启动参数,运行时不可被更改(你可以理解为这个参数是个全局参数,而且被缓存了,如果一旦运行时更改了, 可能会造成整个 jvm 里面的程序奔溃),你只能修改系统的charset, 或者jvm的启动参数里加上 -Dfile.encoding="UTF-8" 来指定,你运行时 setProperty("file.encoding","ISO-8859-1"); 这样是没用的,so,永久的解决办法是:啥时候把这两台机器offline 改编码后再online,然后再手动执行下 data balance。

(2)临时工方案:

不想这么大动干戈,想要临时解决方案,也行,那就需要在咱们自己的业务代码里绕开 jvm 提供的默认 file.encoding 编码,自己指定编码:

?
1
2
3
BufferedReader in =newBufferedReader(newFileReader(path.toString()));
换成:
BufferedReader in =newBufferedReader((newInputStreamReader(newFileInputStream(path.toString()),"utf-8")));
上面一句是我之前乱码的代码,如果你没有指定读取编码,那么 jvm 会使用自己的 file.encoding,这样就会造成在某些机器上读取文件就乱掉了。下面一句是自己指定编码,这样绕开了 jvm 的默认编码,与 jvm 从此形同陌路~ 

PS:FileReader 貌似没有提供指定编码的构造方法,所以换成了下面的类。

(3)疑问:

为什么之前一直都没乱码,而这次读文件却乱码了呢?

那是因为 hbase 的 Bytes、map 的 fileinputformat key/value、mapreduce 的 context.write 默认都是自己硬编码了 utf-8,做到了 和 jvm 编码无关,所以不会遇到上述问题。

4、深入理解 jvm 的 -Dfile.encoding 参数

上面说了这么多,可能有同学还是不大明白:jvm 的这参数有毛用啊?为毛之前都没听过这玩意呢?

恩,没听过正常,之前我也没听过哈~

(1)从源码开始追踪

在JDK 1.6.0_20的src.zip文件中,查找包含file.encoding字眼的文件.
共找到4个, 分别是:
(a)先上重头戏 java.nio.Charset类:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
publicstaticCharset defaultCharset() {
        if(defaultCharset ==null) {
            synchronized(Charset.class) {
                java.security.PrivilegedAction pa =newGetPropertyAction("file.encoding");
                String csn = (String) AccessController.doPrivileged(pa);
                Charset cs = lookup(csn);
                if(cs !=null)
                    defaultCharset = cs;
                else
                    defaultCharset = forName("UTF-8");
            }
        }
        returndefaultCharset;
    }

在java中,如果没有指定charset的时候,比如new String(byte[] bytes), 都会调用Charset.defaultCharset()的方法,我们可以清楚的看到defaultCharset是只能被初始化一次,这里还是有点小问题的,在多线程并发调用的时候还是会初始话多次,当然后面都是从cache(lookup的函数)里读出来的,问题也不大。
当我们在改变System.getProperties里的file.encoding 的时候,defaultCharset已经被初始化过了,所以不会在调用初始化的代码。
当jvm 启动的时候,load class, 最后调用main函数之前,defaultCharset已经初始化好,而很多函数里都掉用了这个方法象String.getBytes, 还有 InputStreamReader, InputStreamWriter 都是调用了 Charset.defaultCharset()的方法。

(b)java.net.URLEncoder的静态构造方法,    影响到的方法 java.net.URLEncoder.encode(String)

恩,这里也需要注意,之前已经有同学掉坑里去了,请使用:encode(String s, String enc) 方法,此法无侧漏,一觉睡到大天亮~

(c)com.sun.org.apache.xml.internal.serializer.Encoding的getMimeEncoding方法(209行起)

(d)最后一个javax.print.DocFlavor类的静态构造方法

可以看到,系统变量file.encoding影响到
1. Charset.defaultCharset() Java环境中最关键的编码设置
2. URLEncoder.encode(String) Web环境中最常遇到的编码使用
3. com.sun.org.apache.xml.internal.serializer.Encoding 影响对无编码设置的xml文件的读取
4. javax.print.DocFlavor 影响打印的编码

(2)Java's file.encoding property on Windows platform

This property is used for the default encoding in Java, all readers and writers would default to use this property. “file.encoding” is set to the default locale of Windows operationg system since Java 1.4.2. System.getProperty(“file.encoding”) can be used to access this property. Code such as System.setProperty(“file.encoding”, “UTF-8”) can be used to change this property. However, the default encoding can not be changed dynamically even this property can be changed. So the conclusion is that the default encoding can’t be changed after JVM starts. “java -Dfile.encoding=UTF-8” can be used to set the default encoding when starting a JVM. I have searched for this option Java official documentation. But I can’t find it.

5、Refer:

系统变量file.encoding对Java的运行影响有多大?

http://www.blogjava.net/ivanwan/archive/2011/01/31/343810.html

Setting the default Java character encoding?

http://stackoverflow.com/questions/361975/setting-the-default-java-character-encoding

Hadoop的map/reduce作业输入非UTF-8编码数据的处理原理

http://blogread.cn/it/article/3736?f=wb

© 著作权归作者所有

共有 人打赏支持
五大三粗
粉丝 155
博文 892
码字总数 4545120
作品 0
广州
程序员
加载中

评论(1)

张忠友
分析得很清楚,学习了
Win10系统下eclipse的基本配置 编码格式 classpath原理 import工作机制

eclipse是用来编译java文件的一个软件 国内也有汉化版,我也不知道该怎么上传……忘了从哪里搞来了 不过用了一段时间就还原原来的配置了因为网上的教程之类的方法都是英文版的 为了和网上的配...

codingcoge ⋅ 03/17 ⋅ 0

JNI开发流程与引用数据类型的处理

今天我们来看下Java JNI,先看下维基百科给的定义, JNI, Java Native Interface, Java本地接口,是一种编程框架,使得Java虚拟机中的Java程序可以调用本地应用或库,也可以被其他程序调用。...

juexingzhe ⋅ 05/04 ⋅ 0

OpenRASP v0.31 支持 resin 服务器并解决 JDK 兼容性

OpenRASP v0.31 已发布,OpenRASP 是百度安全推出的一款免费、开源的自适应安全产品 更新如下: 重大变更 Java 版本 在升级前,用户需要手动删除 rasp/conf/rasp-log4j.xml 文件 待应用启动后...

OpenRASP ⋅ 05/22 ⋅ 0

Android JNI学习(四)——JNI的常用方法的中文API

本系列文章如下: Android JNI(一)——NDK与JNI基础 Android JNI学习(二)——实战JNI之“hello world” Android JNI学习(三)——Java与Native相互调用 Android JNI学习(四)——JNI的常用方法...

隔壁老李头 ⋅ 05/09 ⋅ 0

关于 Java IO(二):从面向字节到面向字符

在上一篇文章中,我们以面向字节的输入为例,介绍了 Java 中 IO 的结构。在这篇文章中,主要介绍面向字节的输入输出是怎么转换到面向字符的输入输出的。 面向字符的输入输出指的是输入输出的...

Happioo ⋅ 04/09 ⋅ 0

win10下java的jdk安装和环境变量配置

首先需要去JAVA官网下载jdk http://www.oracle.com/technetwork/java/javase/downloads/jdk9-downloads-3848520.html 下载的是jdk 9 与以往的环境配置不一样 jdk是java的核心 是java软件的开...

codingcoge ⋅ 03/09 ⋅ 0

Maven的安装文字版(Windows/Linux/Mac)

以下内容引用自https://ayayui.gitbooks.io/tutorialspoint-maven/content/book/mavenenvironment_setup.html,安装信息更新到最新版本: Maven是基于Java的工具,所以首要必须的要求是你的电...

easonjim ⋅ 2017/05/07 ⋅ 0

加强Docker容器与Java 10集成

很多运行在Java虚拟机(JVM)中的应用,包括数据服务如Apache Spark和Kafka以及传统企业应用,都运行在容器中。最近,运行在容器里的JVM出现了由于内存和CPU资源限制和使用率导致性能损失问题...

java高级架构牛人 ⋅ 06/04 ⋅ 0

14、Java并发性和多线程-Java ThreadLocal

以下内容转自http://ifeve.com/java-theadlocal/: Java中的ThreadLocal类可以让你创建的变量只被同一个线程进行读和写操作。因此,尽管有两个线程同时执行一段相同的代码,而且这段代码又有...

easonjim ⋅ 2017/06/16 ⋅ 0

Tomcat与Jre绿色环境配置(生产环境)

Tomcat与Jre绿色环境配置(生产环境)博客分类: Apache Java jreapachetomcat Tomcat运行时需要jre的支持,一般有两种方式,一种是用jdk带的jre,另一种无需安装jdk只利用jre。这两种方式的...

thinkyoung ⋅ 2015/12/31 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

对于程序员的招聘问题,作为软件人的一些吐槽和建议

作为软件人,找工作有时候似乎挺苦逼的。 说真的,让我去掉前面这句中“似乎”二字吧。就是苦逼!很多人都曾抱怨处在招聘的一方很糟糕——我们没有任何可靠的方式来甄别会写代码并且写得好的...

老道士 ⋅ 28分钟前 ⋅ 0

HDFS原理学习

一、概述 1、 Hadoop整合了众多的文件系统,首先提供了一个高层的文件系统抽象org.apache.hadoop.fs.FileSystem。然后有各个文件系统的实现类。 2、Hadoop是JAVA编写的,不同文件系统之间的交...

cjxcloud ⋅ 32分钟前 ⋅ 0

Linux下MySQL表名不区分大小写的设置方法(抄袭别人的)

Linux下MySQL表名不区分大小写的设置方法 MySQL表名不区分大小写的设置方法 在用centox安装mysql后,把项目的数据库移植了过去,发现一些表的数据查不到,排查了一下问题,最后发现是表名的大...

随风而浮沉 ⋅ 37分钟前 ⋅ 0

ubuntu下安装宋体simsun

sudo cp simsun.ttc /usr/share/fonts cd /usr/share/fonts sudo chmod 644 simsun.ttc 更新字体缓存: 代码: sudo mkfontscale 代码: sudo mkfontdir 代码: sudo fc-cache -fsv 安装chrome扩......

wangxuwei ⋅ 38分钟前 ⋅ 0

利用 ssh 传输文件

Linux 下一般可以用 scp 命令通过 ssh 传送文件: #把服务器上的 /home/user/a.txt 发送到本机的 /var/www/local_dir 目录下scp username@servername:/home/user/a.txt /var/www/local_dir...

大灰狼时间 ⋅ 48分钟前 ⋅ 0

web3j教程:android和java程序员如何使用web3j开发区块链以太坊

如何使用web3j为Java应用或Android App增加以太坊区块链支持,本教程内容即涉及以太坊中的核心概念,例如账户管理包括账户的创建、钱包创建、交易转账,交易与状态、智能合约开发与交互、过滤...

智能合约 ⋅ 今天 ⋅ 0

web3j开发java或android以太坊智能合约快速入门

web3j简介 web3j是一个轻量级、高度模块化、响应式、类型安全的Java和Android类库提供丰富API,用于处理以太坊智能合约及与以太坊网络上的客户端(节点)进行集成。 可以通过它进行以太坊区块链...

笔阁 ⋅ 今天 ⋅ 0

一起读书《深入浅出nodejs》-异步I/O

异步I/O “异步”这个名词其实很早就诞生了,但它大规模流行却是在Web 2.0浪潮中,它伴随着AJAX的第一个A(Asynchronous)席卷了Web。 为什么要异步I/O 关于异步I/O为何在Node里如此重要,这与...

小草先森 ⋅ 今天 ⋅ 0

JVM各种问题

1、如果启动什么都不设,会怎样? 先来看一个命令 [root@localhost bin]# java -XX:+PrintCommandLineFlags -version -XX:InitialHeapSize=29899008 -XX:MaxHeapSize=478384128 -XX:+PrintCo......

算法之名 ⋅ 今天 ⋅ 0

SAS笔记-宏2

宏是一种文本,一般来说其编译是在程序执行之前。 宏变量的创建 %let语句 %let macro_variables = text; %let是常见的宏变量建立方式,其编译就在执行前。如下例中,想要宏变量test等于数据集...

tonorth123 ⋅ 今天 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部