parent
68f472825e
commit
fb1b3a8a53
@ -1,90 +0,0 @@ |
||||
```java |
||||
|
||||
|
||||
/** |
||||
* 我想统计两个类中方法执行耗时,BootActivity 中的统计没什么问题, |
||||
* 但是 CommonRequest 类中的统计直接导致应用崩溃 |
||||
* 类转换异常?很萌 |
||||
* AspectJ 才学,希望大佬能帮帮我,不胜感激 |
||||
*/ |
||||
|
||||
com.xxx.BootActivity |
||||
public class BootActivity extends Activity{ |
||||
onCreate(){ |
||||
CommonRequest.method1(this); |
||||
CommonRequest.method2(this); |
||||
//... |
||||
} |
||||
} |
||||
|
||||
com.yyy.CommonRequest |
||||
public class CommonRequest{ |
||||
public static void mothod1(Context context){ |
||||
//网络请求 |
||||
new HttpRequest(context).callback(new CallBack()){ |
||||
// |
||||
} |
||||
} |
||||
} |
||||
|
||||
//切面类 |
||||
@Aspect |
||||
public class MethodTimeTracker { |
||||
|
||||
//这个切点执行正常 |
||||
@Around("execution( * com.xxx.BootActivity.* (..))") |
||||
public void bootTracker(ProceedingJoinPoint joinPoint) throws Throwable { |
||||
long beginTime = SystemClock.currentThreadTimeMillis(); |
||||
joinPoint.proceed(); |
||||
long endTime = SystemClock.currentThreadTimeMillis(); |
||||
long dx = endTime - beginTime; |
||||
Log.i("2333", joinPoint.getSignature().getDeclaringType().getName() + "#" + joinPoint.getSignature().getName() |
||||
+ " " + dx + " ms"); |
||||
} |
||||
|
||||
//这个切点直接导致应用崩溃 |
||||
@Around("execution( * com.yyy.CommonRequest.* (..))") |
||||
public void requestTracker(ProceedingJoinPoint joinPoint) throws Throwable { |
||||
long beginTime = SystemClock.currentThreadTimeMillis(); |
||||
joinPoint.proceed(); |
||||
long endTime = SystemClock.currentThreadTimeMillis(); |
||||
long dx = endTime - beginTime; |
||||
Log.i("2333", joinPoint.getSignature().getDeclaringType().getName() + "#" + joinPoint.getSignature().getName() |
||||
+ " " + dx + " ms"); |
||||
} |
||||
} |
||||
|
||||
Log 日志: |
||||
java.lang.RuntimeException: Unable to start activity ComponentInfo{.BootActivity}: java.lang.ClassCastException: BootActivity cannot be cast to cn.shuhe.dmnetwork.network.CjjHttpRequest |
||||
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2913) |
||||
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3048) |
||||
at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:78) |
||||
at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:108) |
||||
at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:68) |
||||
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1808) |
||||
at android.os.Handler.dispatchMessage(Handler.java:106) |
||||
at android.os.Looper.loop(Looper.java:193) |
||||
at android.app.ActivityThread.main(ActivityThread.java:6669) |
||||
at java.lang.reflect.Method.invoke(Native Method) |
||||
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493) |
||||
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:858) |
||||
Caused by: java.lang.ClassCastException: BootActivity cannot be cast to CjjHttpRequest |
||||
at cn.shuhe.projectfoundation.CommonRequest$AjcClosure15.run(CommonRequest.java:1) |
||||
at org.aspectj.runtime.reflect.JoinPointImpl.proceed(JoinPointImpl.java:149) |
||||
at com.dataseed.cjjanalytics.aspect.MethodTimeTracker.requestTracker(MethodTimeTracker.java:29) |
||||
at cn.shuhe.projectfoundation.CommonRequest.checkDeviceInfo(CommonRequest.java:223) |
||||
at com.dataseed.huanbei.ui.BootActivity.startUp(BootActivity.java:296) |
||||
at com.dataseed.huanbei.ui.BootActivity.onCreate_aroundBody2(BootActivity.java:129) |
||||
at com.dataseed.huanbei.ui.BootActivity$AjcClosure3.run(BootActivity.java:1) |
||||
at org.aspectj.runtime.reflect.JoinPointImpl.proceed(JoinPointImpl.java:149) |
||||
at com.dataseed.cjjanalytics.aspect.CjjTraceAspect.weaveActivityOnCreateJoinPoint(CjjTraceAspect.java:225) |
||||
at com.dataseed.huanbei.ui.BootActivity.onCreate(BootActivity.java:126) |
||||
at android.app.Activity.performCreate(Activity.java:7136) |
||||
at android.app.Activity.performCreate(Activity.java:7127) |
||||
|
||||
|
||||
类转换异常是怎么肥事? |
||||
|
||||
|
||||
``` |
||||
|
@ -0,0 +1,11 @@ |
||||
--- |
||||
String |
||||
--- |
||||
|
||||
#### 目录 |
||||
|
||||
1. 思维导图 |
||||
2. String |
||||
- |
||||
3. 参考 |
||||
|
@ -0,0 +1,106 @@ |
||||
--- |
||||
final |
||||
--- |
||||
|
||||
#### 目录 |
||||
|
||||
1. final |
||||
- 引言 |
||||
- 内存语义 |
||||
- 优势所在 |
||||
- 天生线程安全 |
||||
- 编译器优化 |
||||
- 参考 |
||||
|
||||
#### 引言 |
||||
|
||||
说起 final 关键字,脱口而出就是修饰变量变量不可变、修饰方法方法不可重写、修饰类类不可继承。 |
||||
|
||||
或许你还听过 final 能够提高效率,那如何提高效率的呢?方法内联?方法内联又是个什么东西呢? |
||||
|
||||
带着疑问来探索一下 final。 |
||||
|
||||
#### 内存语义 |
||||
|
||||
##### 写 final 域的重排序规则 |
||||
|
||||
写 final 域的重排序规则禁止把 final 域的写重排序到构造函数之外,这个规则的实现包含两个方面: |
||||
|
||||
1. JVM 禁止编译器把 final 域的写重排序到构造函数之外 |
||||
2. 编译器会在 final 域的写之后,构造函数 return 之前,插入一个 StoreStore 内存屏障,这个屏障禁止处理器把 final 域的写重排序到构造函数之外 |
||||
|
||||
写 final 域的重排序规则可以确保:在对象引用为任何线程可见之前,对象的 final 域已经被正确初始化过了,而普通域不具有这个保证。 |
||||
|
||||
##### 读 final 域的重排序规则 |
||||
|
||||
在一个线程中,初次读对象引用与初次读改对象包含的 final 域,JMM 禁止处理器重排序这两个操作。编译器会在读 final 域操作的前面插入一个 LoadLoad 内存屏障。 |
||||
|
||||
读 final 域的重排序可以确保:在读一个对象的 final 域之前,一定会先读包含这个 final 域的对象引用。 |
||||
|
||||
#### 优势 |
||||
|
||||
##### 线程安全 |
||||
|
||||
这个不用多说,不可变对象天生线程安全。 |
||||
|
||||
##### 编译器优化 |
||||
|
||||
经常能听到别人说 final 能提高效率,在于编译器在对 final 修饰的方法做了方法内联。这句话对与否先不讨论,先来了解一下方法内联是什么鬼? |
||||
|
||||
在了解方法内联之前,先来了解一下函数调用过程: |
||||
|
||||
1. 首先会有个执行栈,存储它们的局部变量、方法名、动态链接地址 |
||||
2. 当一个方法调用,一个新的栈桢会被加到栈顶,分配的本地变量和参数会存储在这个栈桢 |
||||
3. 调转到目标方法代码执行 |
||||
4. 方法返回的时候,本地方法和参数被销毁,栈顶被移除 |
||||
5. 返回原来的地址执行 |
||||
|
||||
其实这就是虚拟机栈执行方法的内存模型。函数调用需要一定的时间开销和空间开销,当一个方法被频繁调用时,这个时间和空间开销会相对变的很大。根据二八原则,80% 的性能消耗其实是发生在 20% 的代码上,对热点代码的针对优化可以提升整体系统的性能。 |
||||
|
||||
然后我们在看看方法内联到底是什么?其实可以用以下例子粗鄙的解释: |
||||
|
||||
```java |
||||
private int add2(int x1 , int x2 , int x3 , int x4) { |
||||
return add1(x1 , x2) + add1(x3,x4); |
||||
} |
||||
|
||||
private int add1(int x1 , int x2) { |
||||
return x1 + x2; |
||||
} |
||||
``` |
||||
|
||||
在运行一段时间后,代码可能就变成下面这样了: |
||||
|
||||
```java |
||||
private int add2(int x1 , int x2 , int x3 , int x4) { |
||||
//return add1(x1 , x2) + add1(x3,x4); |
||||
return x1 + x2 + x3 + x4; |
||||
} |
||||
``` |
||||
|
||||
嘿,看来方法内联就是把调用的方法函数代码复制到了调用函数内,减少因函数调用而带来的开销。 |
||||
|
||||
但是方法内联也是有条件的: |
||||
|
||||
1. 热点代码 |
||||
2. 方法体不能太大 |
||||
|
||||
方法内联会导致拷贝代码副本过多,代码占用内存增加,所以方法体不能太大。 |
||||
|
||||
其次由于方法可能被继承,导致需要类型检查而没有达到性能的效果,所以想要对热点代码进行方法内联,最好尽量使用 final、private、static 这些修饰方法,避免因为继承而导致额外的类型检查。 |
||||
|
||||
说到这,其实就能理解 final 能够提高效率的原因在于哪了,**但是如果想通过 final 来提升性能,那几乎是不太可能。** |
||||
|
||||
现在,让我来回答一下 final 能够提高性能这个说法? |
||||
|
||||
emmmm,这样的说法太片面。final 的确能够提升性能,但毕竟微乎其微。final 修饰的变量值多数情况下就能在编译阶段确定下来,这就可能导致某些代码被编译器优化掉。其次,关于方法内联的说法,也的确存在。但是方法内联也是需要条件的,首先是被频繁调用的热点代码,方法内联能够减少方法调用所带来的性能消耗;其次是方法体不能过大,原因很简单,方法内联将会导致拷贝代码更多,代码占用更多的内存空间。最后才是方法最好能被 final、private、static 修饰,这样就能避免过多的参数检查、类型检查等等。我们更应该关注 final 所带来的特性,比如属性不可变、方法不可重写、类不可继承,想要通过 final 来提升性能,几乎是不可能的事。 |
||||
|
||||
#### 参考 |
||||
|
||||
[深入理解Java中的final关键字](http://www.importnew.com/7553.html) |
||||
|
||||
[Final of Java,这一篇差不多了](https://www.jianshu.com/p/f68d6ef2dcf0) |
||||
|
||||
[final修饰递归方法会提高效率吗?](https://www.zhihu.com/question/66083114) |
||||
|
||||
[Java 方法内联](https://www.cnblogs.com/xyz-star/p/10152564.html) |
Loading…
Reference in new issue