之前我在一篇blog中写过如何使用多语言工具包,见http://www.cnblogs.com/yanxiaodi/p/3800767.html

在WinEcos社区也发布过一篇详细的文章介绍多语言工具包的使用,但因社区改版那篇文章已经找不到了。

当时写的时候还没有出Win10的SDK,都是基于UAP框架写的。微软早已经发布了Win10的SDK,相应的项目结构也发生了变化,以前分为两个项目通过Share项目共享代码的方式被抛弃,改为合并为一个项目,真正实现了一套代码兼容PC和Mobile两个平台,我已经基于Win10 10586的SDK发布了Currency Exchanger的新版本,下载地址:https://www.microsoft.com/store/apps/9WZDNCRDQ91S

在开发Currency Exchanger的过程中,我又整理了一下支持多语言的问题,记录于此。

一、安装多语言工具包

使用VS2015开发UWP不能再使用老版3.0的多语言工具包,而应该使用新版的V4.0beta,这个还不是正式版,所以兼容性有问题,无法与V3.0共存,安装之后也无法再用VS2013或VS2015打开WP8.1之前的项目,所以安装之前请慎重!请慎重!请慎重!重要的事情说三遍。

我们在开发者后台的下载栏目可以找到多语言工具包的下载页面:https://dev.windows.com/zh-cn/develop/multilingual-app-toolkit

但是!截至到2015年12月31日,这个页面所下载的中文多语言工具包仍然是V3.0,而不是最新的V4.0beta,就算安装了,也无法在UWP项目中应用。

最新版的下载地址在此:

https://visualstudiogallery.msdn.microsoft.com/6dab9154-a7e1-46e4-bbfa-18b5e81df520

这也是我一直吐槽MSDN的原因之一,找个东西累死了,官方的东西都不好找。

还有一种方式,直接在VS2015的扩展里搜索Multilingual App Toolkit,主要要有空格,不然搜不到:

要安装V4.0beta这个。下面那个是旧版的,这两个无法共存。

二、启用多语言工具包

还是做个例子吧。新建一个MultilingualDemo项目,VS2015工具菜单-Multilingual App Toolkit -启用选定内容

会收到提示:1>  未启用项目"MultilingualDemo"-没有可本地化的资源被发现。

这是因为没有发现咳本地化的资源,双击Package.appxmanifest打开,设置一个默认语言,如果在设计的时候就想支持多语言,最好默认语言设置为英语,输入en-US:

然后在项目中添加一个Strings文件夹,再在其下添加一个en-US文件夹,这个文件夹名字要和默认语言代码保持一致,如果默认语言是zh-CN,那就建一个zh-CN的文件夹。

在这个目录下添加一个Resources.resw资源文件,在这里面编辑所需要的字符串:

添加几个资源,注意,如果是要显示在界面上的,可以根据控件的属性来设置,如TextBlock的文字是Text属性,那资源的名字就命名为HelloWorld.Text,Button的文字是Content属性,所以命名为ClickMe.Content,另外我还加了一个AppName,用于在代码中使用。

再次 VS2015工具菜单-Multilingual App Toolkit -启用选定内容

这次可以正常启用了:

1> 项目"MultilingualDemo"已启用。 该项目的来源,文化是 'en-US' [英语(美国)]。

三、在XAML界面上使用语言资源

在Page中放一个 TextBlock,一个Button,一个ComboBox,设置其x:Uid(资源标识符,注意不是x:Name)属性,这样控件就可以根据资源找到其对应的内容:

  1. <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}" DataContext="{StaticResource DesignVM}">
  2. <TextBlock x:Name="pageTitle" Grid.Column="1" Margin="100" Text="{Binding Title}" />
  3. <TextBlock x:Name="textBlock" x:Uid="HelloWorld" HorizontalAlignment="Left" Margin="100,156,0,0" TextWrapping="Wrap" VerticalAlignment="Top"/>
  4. <Button x:Name="button" x:Uid="ClickMe" HorizontalAlignment="Left" Margin="100,181,0,0" VerticalAlignment="Top"/>
  5. <ComboBox x:Name="comboBox" x:Uid="ChangeLanguage" HorizontalAlignment="Left" Margin="100,249,0,0" VerticalAlignment="Top" Width="120"/>
  6.  
  7. </Grid>

四、在代码中使用语言资源

添加一个

  1. public static class AppResources
  2. {
  3. private static ResourceLoader CurrentResourceLoader
  4. {
  5. get { return _loader ?? (_loader = ResourceLoader.GetForCurrentView("Resources")); }
  6. }
  7.  
  8. private static ResourceLoader _loader;
  9. private static readonly Dictionary<string, string> ResourceCache = new Dictionary<string, string>();
  10.  
  11. public static string GetString(string key)
  12. {
  13. string s;
  14. if (ResourceCache.TryGetValue(key, out s))
  15. {
  16. return s;
  17. }
  18. else
  19. {
  20. s = CurrentResourceLoader.GetString(key);
  21. ResourceCache[key] = s;
  22. return s;
  23. }
  24. }
  25.  
  26. /// <summary>
  27. /// AppName
  28. /// </summary>
  29. public static string AppName
  30. {
  31. get
  32. {
  33. return CurrentResourceLoader.GetString("AppName");
  34. }
  35. }
  36. }

打开MainPage_Model.cs,取消OnBindedViewLoad方法的注释,在里面添加以下代码:

  1. /// <summary>
  2. /// This will be invoked by view when the view fires Load event and this viewmodel instance is already in view's ViewModel property
  3. /// </summary>
  4. /// <param name="view">View that firing Load event</param>
  5. /// <returns>Task awaiter</returns>
  6. protected override Task OnBindedViewLoad(MVVMSidekick.Views.IView view)
  7. {
  8. this.Title = AppResources.AppName;
  9. return base.OnBindedViewLoad(view);
  10. }

现在运行看看:

现在默认语言是en-US。

五、翻译成本地化资源

如果没安装多语言工具包的话,可以在Strings目录下手动添加对应语言的文件夹和资源文件,不过有多语言工具包的话这个工作就变得很容易了,在项目上右键,添加翻译语言:

在这里可以选择要添加什么语言,选择简体中文:

这里建议只选择zh-Hans就可以了,不用选择zh-CN,因为语言与资源的匹配非常复杂,语言标记存在多种可能影响匹配优先级的可选组件,建议让系统来选择,MSDN文档是这么说的:(https://msdn.microsoft.com/zh-cn/library/windows/apps/xaml/mt607079.aspx

使用某个语言标记的可选组件的示例有:

  • 用于禁止脚本语言的脚本。例如,en-Latn-US 与 en-US 匹配。
  • 区域。例如,en-US 与 en 匹配。
  • 变体。例如,de-DE-1996 与 de-DE 匹配。
  • -x 和其他扩展名。例如,en-US-x-Pirate 与 en-US 匹配。

对于不采用 xx 或 xx-yy 形式的语言标记,也存在许多组件,且并非全部匹配。

  • zh-Hant 与 zh-Hans 不匹配。

Windows 以一个标准的易于理解的方式排定语言匹配的优先顺序。例如,按优先顺序,en-US 依次与 en-US、en、en-GB 等等匹配。

  • Windows 执行跨区域匹配。例如,en-US 与 en-US 匹配,然后依次与 en、en-* 匹配。
  • Windows 提供了一些额外数据,它们可用于区域内(如某种语言的主要区域)的相关性匹配。例如,fr-FR 比 fr-CA 更匹配 fr-BE。
  • 如果你使用 Windows API,则可以免费获取日后 Windows 在语言匹配方面的任何改进。

在与列表中的首个语言匹配之后才会与列表中第二个语言匹配,对于其他区域变体也是如此。例如,如果应用程序语言为 en-US,则会先于 fr-CA 资源选择用于 en-GB 的资源。仅当没有用于 en 形式的资源时才选择用于 fr-CA 的资源。

应用程序语言列表设置为用户的区域变体,尽管该变体不同于应用提供的区域变体。例如,如果用户使用 en-GB,但应用支持 en-US,则应用程序语言列表将包含 en-GB。这将确保日期、时间和数字的格式更接近用户的期望 (en-GB),但仍然使用应用支持的语言 (en-US) 加载 UI 资源(由于语言匹配)。

除非想把本地化做的非常完善,为中国用户和新加坡用户都提供不同的语言资源,否则只提供一种中文简体就够了。

选择后,Strings目录下会自动添加中文简体资源文件:

但是中文的资源里还是空的,我们需要翻译一下英文资源。右键单击MultilingualDemo.zh-Hans.xlf,选择打开方式:

现在可以输入翻译了,如果懒的话就点击菜单翻译按钮调用Bing的翻译接口自动翻译一下,保存。

重新编译生成一下项目,多语言工具包会根据默认资源去填充其他语言的资源文件:

现在可以看到中文的资源文件里也已经有了。翻译的时候要注意,字符串有几种状态,新、翻译、需要评审、最终等,可以根据这几种状态灵活切换显示哪些字符串来处理。

重新运行项目,如果我们的电脑系统默认是中文语言,那app应该已经变成中文界面了。如果用户电脑或手机默认语言是英语,则会调用en-US,如果是其他语言,则会调用默认语言en-US。

六、更改首选语言

App应具有可更改语言的设置。为了保存用户的首选语言,需要使用Windows.Storage.ApplicationData.Current.LocalSettings来保存用户的设置,关于如何使用这个来保存配置网上有很多介绍,这里就不详细介绍了。

在MainPage.xaml里头部添加以下命名空间:

  1. xmlns:Interactivity="using:Microsoft.Xaml.Interactivity" xmlns:Core="using:Microsoft.Xaml.Interactions.Core"

把ComboBox代码改成这样:

  1. <ComboBox x:Name="comboBox" x:Uid="ChangeLanguage" HorizontalAlignment="Left" Margin="100,249,0,0" VerticalAlignment="Top" Width="120"
  2. ItemsSource="{Binding LanguageList}" SelectedIndex="{Binding LanguageCodeIndex, Mode=TwoWay}" >
  3. <ComboBox.ItemTemplate>
  4. <DataTemplate>
  5. <TextBlock Text="{Binding DisplayName}" />
  6. </DataTemplate>
  7. </ComboBox.ItemTemplate>
  8. <Interactivity:Interaction.Behaviors>
  9. <Core:EventTriggerBehavior EventName="SelectionChanged">
  10. <Core:InvokeCommandAction Command="{Binding CommandLanguageChanged}" CommandParameter="{Binding LanguageCodeIndex}"/>
  11. </Core:EventTriggerBehavior>
  12. </Interactivity:Interaction.Behaviors>
  13. </ComboBox>

这里我们主要使用了Windows.Globalization里面的类,Windows.Globalization 还具有作为帮助程序对象提供的 Language 对象。它帮助应用检查有关语言的详细信息,例如,语言的脚本、显示名称和本地名称。主要语言替代PrimaryLanguageOverride是一个简单的替代设置,它用于让用户独立选择语言的应用,或者有充分理由替代默认语言选择的应用。

相应的vm里添加以下代码:

  1. public ObservableCollection<Language> LanguageList
  2. {
  3. get { return _LanguageListLocator(this).Value; }
  4. set { _LanguageListLocator(this).SetValueAndTryNotify(value); }
  5. }
  6. #region Property ObservableCollection<Language> LanguageList Setup
  7. protected Property<ObservableCollection<Language>> _LanguageList = new Property<ObservableCollection<Language>> { LocatorFunc = _LanguageListLocator };
  8. static Func<BindableBase, ValueContainer<ObservableCollection<Language>>> _LanguageListLocator = RegisterContainerLocator<ObservableCollection<Language>>("LanguageList", model => model.Initialize("LanguageList", ref model._LanguageList, ref _LanguageListLocator, _LanguageListDefaultValueFactory));
  9. static Func<ObservableCollection<Language>> _LanguageListDefaultValueFactory = () => { return new ObservableCollection<Language>(); };
  10. #endregion
  11.  
  12. /// <summary>
  13. /// 语言设置
  14. /// </summary>
  15. public int LanguageCodeIndex
  16. {
  17. get { return _LanguageCodeIndexLocator(this).Value; }
  18. set { _LanguageCodeIndexLocator(this).SetValueAndTryNotify(value); }
  19. }
  20. #region Property int LanguageCodeIndex Setup
  21. protected Property<int> _LanguageCodeIndex = new Property<int> { LocatorFunc = _LanguageCodeIndexLocator };
  22. static Func<BindableBase, ValueContainer<int>> _LanguageCodeIndexLocator = RegisterContainerLocator<int>("LanguageCodeIndex", model => model.Initialize("LanguageCodeIndex", ref model._LanguageCodeIndex, ref _LanguageCodeIndexLocator, _LanguageCodeIndexDefaultValueFactory));
  23. static Func<int> _LanguageCodeIndexDefaultValueFactory = () => { return -; };
  24. #endregion
  25.  
  26. public CommandModel<ReactiveCommand, String> CommandLanguageChanged
  27. {
  28. get { return _CommandLanguageChangedLocator(this).Value; }
  29. set { _CommandLanguageChangedLocator(this).SetValueAndTryNotify(value); }
  30. }
  31. #region Property CommandModel<ReactiveCommand, String> CommandLanguageChanged Setup
  32.  
  33. protected Property<CommandModel<ReactiveCommand, String>> _CommandLanguageChanged = new Property<CommandModel<ReactiveCommand, String>> { LocatorFunc = _CommandLanguageChangedLocator };
  34. static Func<BindableBase, ValueContainer<CommandModel<ReactiveCommand, String>>> _CommandLanguageChangedLocator = RegisterContainerLocator<CommandModel<ReactiveCommand, String>>("CommandLanguageChanged", model => model.Initialize("CommandLanguageChanged", ref model._CommandLanguageChanged, ref _CommandLanguageChangedLocator, _CommandLanguageChangedDefaultValueFactory));
  35. static Func<BindableBase, CommandModel<ReactiveCommand, String>> _CommandLanguageChangedDefaultValueFactory =
  36. model =>
  37. {
  38. var resource = "CommandLanguageChanged"; // Command resource
  39. var commandId = "CommandLanguageChanged";
  40. var vm = CastToCurrentType(model);
  41. var cmd = new ReactiveCommand(canExecute: true) { ViewModel = model }; //New Command Core
  42.  
  43. cmd.DoExecuteUIBusyTask(
  44. vm,
  45. async e =>
  46. {
  47. //Todo: Add LanguageChanged logic here, or
  48. await MVVMSidekick.Utilities.TaskExHelper.Yield();
  49. string oldLan = AppSettings.Instance.LanguageCode;
  50. if (vm.LanguageCodeIndex >= )
  51. {
  52. var lan = vm.LanguageList[vm.LanguageCodeIndex];
  53. AppSettings.Instance.LanguageCode = lan.LanguageTag;
  54. ApplicationLanguages.PrimaryLanguageOverride = lan.LanguageTag;
  55. }
  56. })
  57. .DoNotifyDefaultEventRouter(vm, commandId)
  58. .Subscribe()
  59. .DisposeWith(vm);
  60.  
  61. var cmdmdl = cmd.CreateCommandModel(resource);
  62.  
  63. cmdmdl.ListenToIsUIBusy(
  64. model: vm,
  65. canExecuteWhenBusy: false);
  66. return cmdmdl;
  67. };
  68.  
  69. #endregion

属性可以用propvm代码段,命令用propcmd代码段来快速生成。

在MainPage的vm的load事件中初始化语言列表:

  1. if (!LanguageList.Any())
  2. {
  3. var lanList = ApplicationLanguages.ManifestLanguages;
  4. foreach (var lan in lanList)
  5. {
  6. LanguageList.Add(new Language(lan));
  7. }
  8. }
  9. if (!string.IsNullOrEmpty(AppSettings.Instance.LanguageCode))
  10. {
  11. Language userLan = LanguageList.FirstOrDefault(x => x.LanguageTag == AppSettings.Instance.LanguageCode);
  12. LanguageCodeIndex = LanguageList.IndexOf(userLan);
  13. }

如果用户更改语言后,在程序载入时应该按照用户选择的语言来调用,打开App.xaml.cs,在App构造 函数中添加以下代码:

  1. public App()
  2. {
  3. //TODO 这里可以根据用户需要更改语言
  4. if (!string.IsNullOrEmpty(AppSettings.Instance.LanguageCode))
  5. {
  6. ApplicationLanguages.PrimaryLanguageOverride = AppSettings.Instance.LanguageCode;
  7. }
  8. //ApplicationLanguages.PrimaryLanguageOverride = "cs";
  9. //ResourceContext.GetForCurrentView().Reset();
  10. this.InitializeComponent();
  11. this.Suspending += OnSuspending;
  12. }

现在一个具有基本多语言支持、可更改语言的app就完成了。用户选择不同的语言后,重新打开就会重新设置语言。

七、其他

这里是微软官方的一个例子,不过是win8平台的,可以参考:https://code.msdn.microsoft.com/windowsapps/Application-resources-and-cd0c6eaa/

还有一些特殊的情况需要考虑,如果非要做的那么完美的话:

  • 有可能有的语言字符数比较多,导致控件宽度不够,这就需要为每种语言设置控件的宽度,比如创建App_Name.Width的资源;
  • 有可能语言的排列方向不一致,比如阿拉伯语是右对齐,可以设置对齐属性;
  • 有可能图像也需要本地化,那就需要按照资源限定符的格式来定义图片路径:

    标准命名约定为foldername/qualifiername-value_qualifiername-value/filename.qualifiername-value_qualifiername-value.ext,当资源路径为Images/en-US/homeregion-USA/logo.scale-100_contrast-white.png时,应以Images/logo.png的方式来加载。

关于如何使用资源限定符,可参考:https://msdn.microsoft.com/zh-cn/library/windows/apps/xaml/hh965324.aspx

这个页面是一些本地化的最佳实践:https://msdn.microsoft.com/zh-cn/library/windows/apps/xaml/hh967762.aspx

CurrencyExchanger的本地化我也没有做的那么完善,基本就是只翻译了语言,没有考虑宽度啊排列方式这些,工作量太大了。

至于如何翻译这些文字,建议在app里留一个邮件,让志愿者来帮助翻译,可以将xlf文件上右键导出翻译,选择xlf格式或者csv格式,发送给翻译人员翻译,然后再导入进来即可。但这里又会遇到一个问题,导出的csv用excel打开的话,再另存,会丢失里面的双引号。

从xlf文件导出的csv格式是这样的:

除了表头,每个字段两边都有双引号。

这个文件用excel打开再另存后,再用文本编辑软件打开,会变成这样:

两边的引号没有了,导入的时候会提示错误,无法导入。

我想了个笨办法,在excel里编辑csv的时候,修改单元格属性,改为   !"@!" 然后再保存,这样在文本编辑软件里打开会发现一个双引号变成了两个,然后再使用批量替换功能,把"""替换为",同时别忘了把第一行表头的双引号去掉,才能正确导入。

其实如果直接把资源文件里的内容复制到excel里发送给翻译人员翻译,翻译好了再粘贴回来也行,但要是以后再增加修改默认语言的资源时,其他语言也得手动挨个改,不如用多语言工具包自动填充完整。各有利弊。

最后附上demo下载地址:

链接:http://pan.baidu.com/s/1i4jBn8L 密码:idpz

Win10 UWP 开发系列:使用多语言工具包让应用支持多语言的更多相关文章

  1. Win10 UWP开发系列:使用VS2015 Update2+ionic开发第一个Cordova App

    安装VS2015 Update2的过程是非常曲折的.还好经过不懈的努力,终于折腾成功了. 如果开发Cordova项目的话,推荐大家用一下ionic这个框架,效果还不错.对于Cordova.PhoneG ...

  2. Win10 UWP开发系列:实现Master/Detail布局

    在开发XX新闻的过程中,UI部分使用了Master/Detail(大纲/细节)布局样式.Win10系统中的邮件App就是这种样式,左侧一个列表,右侧是详情页面.关于这种 样式的说明可参看MSDN文档: ...

  3. Win10 UWP 开发系列:使用SQLite

    在App开发过程中,肯定需要有一些数据要存储在本地,简单的配置可以序列化后存成文件,比如LocalSettings的方式,或保存在独立存储中.但如果数据多的话,还是需要本地数据库的支持.在UWP开发中 ...

  4. Win10 UWP 开发系列:使用SplitView实现汉堡菜单及页面内导航

    在Win10之前,WP平台的App主要有枢轴和全景两种导航模式,我个人更喜欢Pivot即枢轴模式,可以左右切换,非常方便.全景视图因为对设计要求比较高,自己总是做不出好的效果.对于一般的新闻阅读类Ap ...

  5. Win10 UWP开发系列:解决Win10不同版本的Style差异导致的兼容性问题

    最近在开发一个项目时,遇到了一个奇怪的问题,项目依赖的最低版本是10586,目标版本是14393,开发完毕发布到商店后,很多用户报无法正常加载页面.经查,有问题的都是Win10 10586版本. 我上 ...

  6. Win10 UWP开发系列——开源控件库:UWPCommunityToolkit

    在开发应用的过程中,不可避免的会使用第三方类库.之前用过一个WinRTXamlToolkit.UWP,现在微软官方发布了一个新的开源控件库—— UWPCommunityToolkit 项目代码托管在G ...

  7. Win10 UWP 开发系列:支持异步的SQLite

    上篇文章已经实现了在UWP中使用SQLite作为本地存储,作为移动端的程序,及时响应用户的操作是提高用户体验的重要途径,因此UWP的很多api都是异步的.那么如何使SQLite支持异步呢? 参考SQL ...

  8. Win10 UWP开发系列:开发一个自定义控件——带数字徽章的AppBarButton

    最近有个项目有一个这样的需求,在文章浏览页底部有几个AppBarButton,其中有一个是评论按钮,需要在评论按钮上显示一个红色数字,类似微信的新消息提醒: 这种设计在iOS和Android平台都是很 ...

  9. Win 10 UWP开发系列:设置AppBarButton的图标

    在WP8以前,页面最下面的四个小圆按钮是不支持绑定的,WP8.1 RT之后,系统按钮升级成了AppBarButton,并且支持绑定了.在Win10 UWP开发中,按钮的样式发生了变化,外面的圆圈没有了 ...

随机推荐

  1. Sql Server 2012新特性 Online添加非空栏位.

    我们都知道,Sql Server在一个数据量巨大的表中添加一个非空栏位是比较费心的,缺乏经验的DBA或是开发人员甚至可能鲁莽地直接添加导致阻塞相应业务,甚至可能因为资源欠缺造成实例的全局问题.当然这都 ...

  2. ViewBag 找不到编译动态表达式所需的一种或多种类型,是否缺少引用?

    症状: 类似上面的警告提示,运行程序不会有任何错误,但若干地方都提示警告,并且明明dll的引用都是正确的. 解决方案: 删除:C:\Users\{your computer name}\AppData ...

  3. Python黑帽编程2.5 函数

    Python黑帽编程2.5 函数 写了几节的基础知识,真心感觉有点力不从心.这块的内容说实话,看文档是最好的方式,本人的写作水平,真的是找不出更好的写法,头疼.简单带过和没写一样,写详细了和本系列教程 ...

  4. .NET中那些所谓的新语法之一:自动属性、隐式类型、命名参数与自动初始化器

    开篇:在日常的.NET开发学习中,我们往往会接触到一些较新的语法,它们相对以前的老语法相比,做了很多的改进,简化了很多繁杂的代码格式,也大大减少了我们这些菜鸟码农的代码量.但是,在开心欢乐之余,我们也 ...

  5. [.net 面向对象程序设计深入](6).NET MVC 6 —— 模型、视图、控制器、路由等的基本操作

    [.net 面向对象程序设计深入](6).NET MVC 6 —— 模型.视图.控制器.路由等的基本操作 1. 使用Visual Studio 2015创建Web App (1)文件>新建> ...

  6. IE10,11下_doPostBack未定义错误的解决方法

    出现的原因 .NET2.0和.NET4.0一起发布的浏览器定义文件中有一个错误,它们保存相当一部分浏览器版本的定义.但是浏览器的有些版本(比如IE10,11)则不再在这个范围之内.因此,ASP.NET ...

  7. 通过扩展让ASP.NET Web API支持W3C的CORS规范

    让ASP.NET Web API支持JSONP和W3C的CORS规范是解决"跨域资源共享"的两种途径,在<通过扩展让ASP.NET Web API支持JSONP>中我们 ...

  8. 单节点部署Hadoop教程

    搭建HDFS 增加主机名 我这里仅仅增加了master主机名 [root@10 /xinghl/hadoop/bin]$ cat /etc/hosts 127.0.0.1 localhost 10.0 ...

  9. CentOS On VirtualBox

    背景 后台开发需要随时与服务器交互,本人使用Mac开发.但是不愿意在Mac上直接安装redis以及mysql等等工具.所以选择在VirtualenvBox下安装一个服务器系统,并且使用ssh与其连接. ...

  10. 《深入理解 java虚拟机》学习笔记

    java内存区域详解 以下内容参考自<深入理解 java虚拟机 JVM高级特性与最佳实践>,其中图片大多取自网络与本书,以供学习和参考.