我已经找了一段时间了,寻找描述Dalvik垃圾收集器体系结构的详细设计文档,但是没有出现太多。考虑到GC运行对性能的影响,我很想更好地理解5个具体的问题: 1.在Android中究竟是什么触发了GC?我看到的其他VM实现通常允许在应用程序接收到要运行的信号之前将一定比例的系统内存分配给应用程序。然而,扫描下面的LogCat似乎显示Dalvik GC至少部分地运行--
12-14 11:34:57.753: D/dalvikvm(279): GC_EXTERNAL_ALLOC freed 735 objects / 54272 bytes
in 90ms
12-14 11:34:57.893: D/dalvikvm(279): GC_EXTERNAL_ALLOC freed 256 objects / 12240 bytes
in 61ms
12-14 11:34:57.943: I/jPCT-AE(279): Loading Texture...
12-14 11:34:57.993: D/dalvikvm(279): GC_FOR_MALLOC freed 65 objects / 2840 bytes in
52ms
12-14 11:34:58.013: I/dalvikvm-heap(279): Grow heap (frag case) to 5.039MB for
1048592-byte allocation
12-14 11:34:58.073: D/dalvikvm(279): GC_FOR_MALLOC freed 1 objects / 40 bytes in 59ms
12-14 11:34:58.243: D/dalvikvm(279): GC_EXTERNAL_ALLOC freed 11 objects / 432 bytes in
55ms
12-14 11:34:58.283: I/jPCT-AE(279): Loading Texture...
12-14 11:34:58.333: D/dalvikvm(279): GC_FOR_MALLOC freed 10 objects / 416 bytes in 46ms
12-14 11:34:58.344: I/dalvikvm-heap(279): Grow heap (frag case) to 6.040MB for
1048592-byte allocation
12-14 11:34:58.423: D/dalvikvm(279): GC_FOR_MALLOC freed 2 objects / 80 bytes in 75ms
12-14 11:34:58.563: D/dalvikvm(279): GC_EXTERNAL_ALLOC freed 10 objects / 384 bytes in
47ms
12-14 11:34:58.603: I/jPCT-AE(279): Loading Texture...
12-14 11:34:58.653: D/dalvikvm(279): GC_FOR_MALLOC freed 11 objects / 464 bytes in 44ms
12-14 11:34:58.663: I/dalvikvm-heap(279): Grow heap (frag case) to 7.040MB for
1048592-byte allocation
12-14 11:34:58.743: D/dalvikvm(279): GC_FOR_MALLOC freed 2 objects / 80 bytes in 75ms
12-14 11:34:58.973: I/System.out(279): started document!
...
12-14 11:43:05.393: I/jPCT-AE(279): Memory usage before compacting: 5867 KB used out
of 6215 KB
12-14 11:43:05.453: D/dalvikvm(279): GC_EXPLICIT freed 2560 objects / 145712 bytes in
61ms
12-14 11:43:05.503: D/dalvikvm(279): GC_EXPLICIT freed 295 objects / 21448 bytes in
51ms
12-14 11:43:05.717: I/jPCT-AE(279): Memory usage after compacting: 5705 KB used out of
6215 KB
...
12-14 11:43:05.792: D/dalvikvm(279): GC_EXTERNAL_ALLOC freed 105 objects / 6152 bytes
in 56ms
12-14 11:43:05.855: D/dalvikvm(279): GC_FOR_MALLOC freed 3 objects / 80 bytes in 51ms
...
12-14 11:43:12.863: D/dalvikvm(279): GC_EXTERNAL_ALLOC freed 864 objects / 1099072
bytes in 70ms
12-14 11:43:13.053: D/dalvikvm(279): GC_EXTERNAL_ALLOC freed 45 objects / 1760 bytes
in 55ms
12-14 11:43:14.533: D/dalvikvm(279): GC_EXTERNAL_ALLOC freed 49 objects / 2376 bytes
in 58ms
12-14 11:43:14.933: D/dalvikvm(279): GC_EXTERNAL_ALLOC freed 34 objects / 1408 bytes
in 55ms
12-14 11:43:15.423: D/dalvikvm(279): GC_EXTERNAL_ALLOC freed 13 objects / 504 bytes in
58ms
12-14 11:43:15.953: D/dalvikvm(279): GC_EXTERNAL_ALLOC freed 13 objects / 520 bytes in
56ms
...
12-14 11:43:31.203: I/jPCT-AE(279): Visibility lists disposed!
12-14 11:43:31.203: I/jPCT-AE(279): All texture data unloaded from gpu!
12-14 11:43:31.203: I/jPCT-AE(279): Renderer disposed!
12-14 11:43:31.203: I/jPCT-AE(279): Static references cleared...
...
12-14 11:43:36.943: E/dalvikvm-heap(279): 2964320-byte external allocation too large
for this process.
12-14 11:43:36.953: E/GraphicsJNI(279): VM won't let us allocate 2964320 bytes
12-14 11:43:36.953: D/AndroidRuntime(279): Shutting down VM
12-14 11:43:36.953: W/dalvikvm(279): threadid=1: thread exiting with uncaught
exception (group=0x4001d800)
12-14 11:43:36.973: E/AndroidRuntime(279): FATAL EXCEPTION: main
12-14 11:43:36.973: E/AndroidRuntime(279): android.view.InflateException: Binary XML
file line #33: Error inflating class <unknown>
12-14 11:43:36.973: E/AndroidRuntime(279): at
android.view.LayoutInflater.createView(LayoutInflater.java:513)
12-14 11:43:36.973: E/AndroidRuntime(279): at
com.android.internal.policy.impl.PhoneLayoutInflater.
onCreateView(PhoneLayoutInflater.java:56)
12-14 11:43:36.973: E/AndroidRuntime(279): at
android.view.LayoutInflater.createViewFromTag(LayoutInflater.java:563)
12-14 11:43:36.973: E/AndroidRuntime(279): at
android.view.LayoutInflater.rInflate(LayoutInflater.java:618)
12-14 11:43:36.973: E/AndroidRuntime(279): at
android.view.LayoutInflater.rInflate(LayoutInflater.java:621)
12-14 11:43:36.973: E/AndroidRuntime(279): at
android.view.LayoutInflater.inflate(LayoutInflater.java:407)
12-14 11:43:36.973: E/AndroidRuntime(279): at
android.view.LayoutInflater.inflate(LayoutInflater.java:320)
12-14 11:43:36.973: E/AndroidRuntime(279): at
com.ai.ultimap.views.Manual.onItemClick(Manual.java:467)
12-14 11:43:36.973: E/AndroidRuntime(279): at
android.widget.AdapterView.performItemClick(AdapterView.java:284)
12-14 11:43:36.973: E/AndroidRuntime(279): at
android.widget.AbsListView$PerformClick.run(AbsListView.java:1696)
12-14 11:43:36.973: E/AndroidRuntime(279): at
android.os.Handler.handleCallback(Handler.java:587)
12-14 11:43:36.973: E/AndroidRuntime(279): at
android.os.Handler.dispatchMessage(Handler.java:92)
12-14 11:43:36.973: E/AndroidRuntime(279): at android.os.Looper.loop(Looper.java:123)
12-14 11:43:36.973: E/AndroidRuntime(279): at
android.app.ActivityThread.main(ActivityThread.java:4627)
12-14 11:43:36.973: E/AndroidRuntime(279): at
java.lang.reflect.Method.invokeNative(Native Method)
12-14 11:43:36.973: E/AndroidRuntime(279): at
java.lang.reflect.Method.invoke(Method.java:521)
12-14 11:43:36.973: E/AndroidRuntime(279): at
com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:868)
12-14 11:43:36.973: E/AndroidRuntime(279): at
com.android.internal.os.ZygoteInit.main(ZygoteInit.java:626)
12-14 11:43:36.973: E/AndroidRuntime(279): at dalvik.system.NativeStart.main(Native
Method)
12-14 11:43:36.973: E/AndroidRuntime(279): Caused by:
java.lang.reflect.InvocationTargetException
12-14 11:43:36.973: E/AndroidRuntime(279): at android.widget.ImageView.<init>
(ImageView.java:108)
12-14 11:43:36.973: E/AndroidRuntime(279): at
java.lang.reflect.Constructor.constructNative(Native Method)
12-14 11:43:36.973: E/AndroidRuntime(279): at
java.lang.reflect.Constructor.newInstance(Constructor.java:446)
12-14 11:43:36.973: E/AndroidRuntime(279): at
android.view.LayoutInflater.createView(LayoutInflater.java:500)
12-14 11:43:36.973: E/AndroidRuntime(279): ... 18 more
12-14 11:43:36.973: E/AndroidRuntime(279): Caused by: java.lang.OutOfMemoryError:
bitmap size exceeds VM budget
12-14 11:43:36.973: E/AndroidRuntime(279): at
android.graphics.Bitmap.nativeCreate(Native Method)
12-14 11:43:36.973: E/AndroidRuntime(279): at
android.graphics.Bitmap.createBitmap(Bitmap.java:468)
12-14 11:43:36.973: E/AndroidRuntime(279): at
android.graphics.Bitmap.createBitmap(Bitmap.java:435)
12-14 11:43:36.973: E/AndroidRuntime(279): at
android.graphics.Bitmap.createScaledBitmap(Bitmap.java:340)
12-14 11:43:36.973: E/AndroidRuntime(279): at
android.graphics.BitmapFactory.finishDecode(BitmapFactory.java:488)
12-14 11:43:36.973: E/AndroidRuntime(279): at
android.graphics.BitmapFactory.decodeStream(BitmapFactory.java:462)
12-14 11:43:36.973: E/AndroidRuntime(279): at
android.graphics.BitmapFactory.decodeResourceStream(BitmapFactory.java:323)
12-14 11:43:36.973: E/AndroidRuntime(279): at
android.graphics.drawable.Drawable.createFromResourceStream(Drawable.java:697)
12-14 11:43:36.973: E/AndroidRuntime(279): at
android.content.res.Resources.loadDrawable(Resources.java:1709)
12-14 11:43:36.973: E/AndroidRuntime(279): at
android.content.res.TypedArray.getDrawable(TypedArray.java:601)
12-14 11:43:36.973: E/AndroidRuntime(279): at android.widget.ImageView.<init>
(ImageView.java:118)
12-14 11:43:36.973: E/AndroidRuntime(279): ... 22 more
12-14 11:43:38.763: I/Process(279): Sending signal. PID: 279 SIG: 9正如您所看到的,我在一个~3MB位图加载过程中特别地遇到了一个外部内存错误.这对我来说没有任何意义,因为GC最近运行了,而且没有分配任何东西,因为它应该将VM的容量控制在3MB以内(256 MB)。在这个256 MB的系统RAM中,只有一小部分是在VM崩溃之前实际提供给它的吗?位图加载过程是否有自己的内存分配上限?我知道对象池是在游戏循环中试图避免GC的好方法,但由于不知道究竟是什么触发了Dalvik GC,我们仍然对OS和Google对性能最佳实践的模糊讨论抱有很大的信心。
5.最后,一些源代码..。对于高效的Android编程,我有正确的想法吗?
活动课:
package com.ai.ultimap;
//imports omitted...
public class UltiMapActivity extends Activity {
//Housekeeping
private String viewDriverID = "";
private static final int TUTORIAL = 7;
//visuals
private HomeView hv; //home view
private ConfigView cv; //config view
private MapView mv; //map view
private Manual man; //manual view
private int manCount = 0; //tracks the number of times the manual has been called
//with menu button, ignoring button presses unless value is zero
private PathCreator pcv; //path creator view
private MasterGL mgl; //the gl center
private String pending = "Coming soon...";
private PathCreator draw;
private Surfacer morlock;
// Used to handle pause and resume...
private static UltiMapActivity master;
//XML I/O considerations
private String fXML = "mypaths.xml";
private String sXML = "data was not saved properly...?";
private FileOutputStream fos;
private FileInputStream fis;
private FileWriter fw;
private FileReader fr;
private Date theDate = new Date();
private char[] buf = new char[1];
//Feedback stuffs
private FeedbackController feed;
//tracking you... :)
private WifiStalk stalk;
private long lat;
private long longitude;
//Testing
private DrawView dv;
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.d("me","ultimap created!");
master = null;
mgl = new MasterGL(this); //revisit this later for versatility
man = new Manual(this);
feed = new FeedbackController(this);
stalk = new WifiStalk(this);
draw = new PathCreator(this);
hv = new HomeView(this,draw);
try {
BeanCounter bean = new BeanCounter(this);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (XmlPullParserException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
showDialog(TUTORIAL);
}
@Override
public boolean onKeyDown(int keyCode,KeyEvent e){
if (keyCode == 82){
if (viewDriverID.equals("hv")){
hv.removeHV();
}
else if (viewDriverID.equals("cv")){
cv.removeCV();
}
else if (viewDriverID.equals("mv")){
return true;
}
else if (viewDriverID.equals("pcv")){
return true;
}
if(man.getAddedState() == 0){
//Show the manual code...
System.out.println("View we're coming from: " + this.getVDID());
Log.e("me", "man.getaddedstate does equal 0, should be about to makeMan");
man.makeMan();
}
else if(man.getAddedState() == 2){
man.removeMan();
man.removeMan2();
man.setAddedState(1);
}
else if(man.getAddedState() == 1){
System.out.println("View we're coming from: " + this.getVDID());
man.addMan();
}
}
return true;
}
@Override
protected Dialog onCreateDialog(int id) {
//alerts ommitted for space
}
//Used to track the semantic context of what the Activity is displaying
//Getters/setters for external access ommitted
@Override
protected void onStart(){
super.onStart();
Log.d("me","ultimap started!");
}
@Override
protected void onPause() {
super.onPause();
Log.d("me","ultimap paused!");
if (mgl.getGLview() != null){
mgl.getGLview().onPause();
}
if (draw.getGLV() != null){
draw.getGLV().onPause();
}
}
@Override
protected void onResume() {
super.onResume();
Log.d("me","ultimap resumed!");
stalk.killListener();
if (mgl.getGLview() != null){
mgl.getGLview().onResume();
Log.d("me", "mgl.getGLview is NOT null on resume");
}
else if (mgl.getGLview() == null){
mgl.initGL();
mgl.getGLview().onResume();
Log.d("me", "mgl.getGLview is null on resume");
}
if (draw.getGLV() != null){
draw.getGLV().onResume();
Log.d("me", "draw.getGLV is NOT null on resume");
}
else if (draw.getGLV() == null && draw.getHGL() != null){
draw.pcvInit();
Log.d("me", "draw.getGLV is null on resume");
}
if (hv.getMV() != null && hv.getMV().getGLV() != null){
hv.getMV().getGLV().onResume();
Log.d("me", "map.getGLV is NOT null on resume");
}
else if (hv.getMV() != null && hv.getMV().getGLV() == null &&
hv.getMV().getHGL() != null){
hv.getMV().mvInit();
Log.d("me", "map.getGLV is null on resume");
}
}
@Override
protected void onStop() {
super.onStop();
//feed.getSP().release();
Log.d("me","ultimap stopped!");
}
@Override
protected void onRestart(){
super.onRestart();
Log.d("me","ultimap restarted!");
if (mgl != null){
mgl.initGL();
}
}
@Override
protected void onDestroy(){
super.onDestroy();
Log.d("me","ultimap destroyed!");
mgl.disposeTextures();
if (feed.getSP() != null && feed.getSID() != 0 && feed.getLoaded() ==
true){
feed.getSP().unload(feed.getSID());
feed.getSP().release();
}
}
}教程视图管理器类:
/*
* This class defines an in-app manual which is callable/dismissable
* in a non-invasive way...
*
* http://www.codeproject.com/KB/android/ViewFlipper_Animation.aspx
*http://developer.android.com/reference/android/widget/
*ViewFlipper.html#ViewFlipper%28android.content.Context%29
* http://developer.android.com/resources/articles/avoiding-memory-leaks.html
*/
package com.ai.ultimap.views;
//imports ommitted
public class Manual extends View implements OnItemClickListener{
private UltiMapActivity hUMA;
private ListView lv1;
private ListAdapter la;
private LayoutInflater mInflater;
private Vector<RowData> data;
private TextView tv;
private RelativeLayout holderRL;
private View v;
private View v2;
private int addedState = 0; //tracks whether or not a view has been instantiated,
//and if so whether or not it is the currently visible view
private int addedState2 = 0;
//Grid View stuff
private GridView helpGrid;
//ViewFlipper stuff
private ViewFlipper vf;
private TextView tutTV;
private String mapTutString = "Map View Tutorial Part: ";
private String pcTutString = "Path Creator Tutorial Part: ";
private String tutType;
private TextView counterTV;
private int partCounter = 1;
private float oldTouchValue = 0.0f;
private boolean searchOk = true;
private ImageView floatingImage;
public Manual(UltiMapActivity hAct){
super(hAct);
hUMA = hAct;
holderRL = new RelativeLayout(hUMA);
v = new View(hUMA);
floatingImage = new ImageView(hUMA);
}
//Here we summon and populate the grid view
public void makeMan(){
if (addedState == 0){
Log.e("me", "in makeMan");
mInflater = (LayoutInflater)
hUMA.getSystemService(Activity.LAYOUT_INFLATER_SERVICE);
hUMA.addContentView(holderRL, new
LayoutParams(LayoutParams.MATCH_PARENT,LayoutParams.MATCH_PARENT));
v = mInflater.inflate(R.layout.helpgrid, holderRL, false);
helpGrid = (GridView) v.findViewById(R.id.manGV);
helpGrid.setAdapter(new ImageAdapter(hUMA));
hUMA.addContentView(v, new
LayoutParams(LayoutParams.MATCH_PARENT,LayoutParams.MATCH_PARENT));
helpGrid.setOnItemClickListener(this);
addedState = 2;
}
}
public void addMan(){
if (v != null && addedState == 1){
v.setVisibility(VISIBLE);
v.bringToFront();
addedState = 2;
}
}
public void addMan2(){
if (v2 != null && addedState2 == 1){
v2.setVisibility(VISIBLE);
v2.bringToFront();
addedState2 = 2;
}
}
public void removeMan(){
if (v != null && addedState == 2){
v.setVisibility(GONE);
addedState = 1;
String s = hUMA.getVDID();
if (s.equals("hv")){
hUMA.getHome().addHV();
Log.d("me", "add hjomeview called from anual");
Log.d("me", "hv addedstate : " +
hUMA.getHome().getAddedState());
}
else if (s.equals("cv")){
hUMA.getConfig().addCV();
}
else if (s.equals("mv")){
hUMA.getHome().getMV().mvInit();
}
else if (s.equals("pcv")){
hUMA.getDraw().pcvInit();
}
}
}
public void removeMan2(){
if (v2 != null && addedState2 == 2){
v2.setVisibility(GONE);
addedState2 = 1;
String s = hUMA.getVDID();
if (s.equals("hv")){
hUMA.getHome().addHV();
Log.d("me", "add hjomeview called from manual");
Log.d("me", "hv addedstate : " +
hUMA.getHome().getAddedState());
}
else if (s.equals("cv")){
hUMA.getConfig().addCV();
}
else if (s.equals("mv")){
hUMA.getHome().getMV().mvInit();
}
else if (s.equals("pcv")){
hUMA.getDraw().pcvInit();
}
}
}
//addedstate getters and setters ommitted for space
@Override
public boolean onTouchEvent(MotionEvent touchevent) {
switch (touchevent.getAction())
{
case MotionEvent.ACTION_DOWN:
{
System.out.println("received a touch down at " + touchevent.getX()
+ "," + touchevent.getY());
oldTouchValue = touchevent.getX();
if(this.searchOk==false) return false;
float currentX = touchevent.getX();
if (currentX > (vf.getWidth()/2))
{
vf.setInAnimation(AnimationHelper.inFromRightAnimation());
vf.setOutAnimation(AnimationHelper.outToLeftAnimation());
vf.showNext();
if (partCounter <= 3 && partCounter >= 1){
partCounter++;
}
else if (partCounter == 4){
partCounter = 1;
}
else{
Log.e("me", "partCounter got past 4...");
}
if(tutType.equals("map")){
counterTV.setText(mapTutString + partCounter);
}
else if(tutType.equals("pc")){
counterTV.setText(pcTutString + partCounter);
}
else{
Log.e("me","not getting valid tutType string");
}
}
if (currentX <= (vf.getWidth()/2))
{
vf.setInAnimation(AnimationHelper.inFromLeftAnimation());
vf.setOutAnimation(AnimationHelper.outToRightAnimation());
vf.showPrevious();
if (partCounter >= 2 && partCounter <= 4){
partCounter--;
}
else if (partCounter == 1){
partCounter = 4;
}
else{
Log.e("me", "partCounter got below 1...");
}
if(tutType.equals("map")){
counterTV.setText(mapTutString + partCounter);
}
else if(tutType.equals("pc")){
counterTV.setText(pcTutString + partCounter);
}
else{
Log.e("me","not getting valid tutType string");
}
}
break;
}
case MotionEvent.ACTION_UP:
{
//nothing to do here
}
}
return false;
}
public void setUserText(String str){
tv.setText(str);
}
private class CustomTV extends TextView{
private String content = "";
public CustomTV(Context c, String str){
super(c);
content = str;
this.setText(content);
}
}
/**
* Data type used for custom adapter. Single item of the adapter.
*/
private class RowData {
protected String mItem;
protected String mDescription;
RowData(String item, String description){
mItem = item;
mDescription = description;
}
@Override
public String toString() {
return mItem + " " + mDescription;
}
}
private class CustomAdapter extends ArrayAdapter<RowData> {
public CustomAdapter(Context context, int resource,
int textViewResourceId, List<RowData> objects) {
super(context, resource, textViewResourceId, objects);
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder = null;
//widgets displayed by each item in your list
TextView item = null;
TextView description = null;
//data from your adapter
RowData rowData= getItem(position);
//we want to reuse already constructed row views...
if(null == convertView){
convertView = mInflater.inflate(R.layout.custom_row, null);
holder = new ViewHolder(convertView);
convertView.setTag(holder);
}
holder = (ViewHolder) convertView.getTag();
item = holder.getItem();
item.setText(rowData.mItem);
description = holder.getDescription();
description.setText(rowData.mDescription);
return convertView;
}
}
/**
* Wrapper for row data.
*
*/
private class ViewHolder {
private View mRow;
private TextView description = null;
private TextView item = null;
public ViewHolder(View row) {
mRow = row;
}
public TextView getDescription() {
if(null == description){
description = (TextView) mRow.findViewById(R.id.cbox);
}
return description;
}
public TextView getItem() {
if(null == item){
item = (TextView) mRow.findViewById(R.id.cbox2);
}
return item;
}
}
@Override
public void onItemClick(AdapterView<?> arg0, View arg1, int position, long id) {
v.setVisibility(GONE);
if (addedState2 == 0){
hUMA.addContentView(this,DefineLayoutParams.getParams(DefineLayoutParams.getMM()));
//this is why the onTouch only starts lsitening at this point
if (position == 0){
v2 = mInflater.inflate(R.layout.flipper, holderRL, false);
vf = (ViewFlipper) v2.findViewById(R.id.manFlipperVF);
tutTV = (TextView) v2.findViewById(R.id.manDescriptionTV);
counterTV = (TextView) v2.findViewById(R.id.mapviewtutCounterTV);
tutTV.setText("Map View Instructions: ...");
counterTV.setText(mapTutString + partCounter);
tutType = "map";
}
else if (position == 1){
v2 = mInflater.inflate(R.layout.flipperpc, holderRL, false);
vf = (ViewFlipper) v2.findViewById(R.id.manFlipperpcVF);
tutTV = (TextView) v2.findViewById(R.id.manDescriptionpcTV);
counterTV = (TextView) v2.findViewById(R.id.manFlipperCounterpcTV);
tutTV.setText("Path Creator Tutorial:...");
counterTV.setText(pcTutString + partCounter);
tutType = "pc";
}
addedState2 = 2;
hUMA.addContentView(v2, DefineLayoutParams.getParams(DefineLayoutParams.getWW()));
}
else if(addedState2 == 1){
v2.setVisibility(VISIBLE);
addedState2 = 2;
}
}
public String getTutType(){
return tutType;
}
}教程视图Flipper XML:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
>
<ScrollView
android:id="@+id/manDerscriptionSV"
android:layout_width="match_parent"
android:layout_height="200px"
>
<TextView
android:id="@+id/manDescriptionTV"
android:text="Coming Soon..."
android:layout_width="match_parent"
android:layout_height="wrap_content"
/>
</ScrollView>
<TextView
android:id="@+id/mapviewtutCounterTV"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Map View Tutorial Part: "
android:gravity="center"
android:layout_below="@id/manDerscriptionSV"
/>
<ViewFlipper
android:id="@+id/manFlipperVF"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@id/mapviewtutCounterTV"
>
<ImageView
android:id="@+id/mapviewtut1"
android:src="@drawable/mapviewtutflipper1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
/>
<ImageView
android:id="@+id/mapviewtut2"
android:src="@drawable/mapviewtutflipper2"
android:layout_width="match_parent"
android:layout_height="wrap_content"
/>
<ImageView
android:id="@+id/mapviewtut3"
android:src="@drawable/mapviewtutflipper3"
android:layout_width="match_parent"
android:layout_height="wrap_content"
/>
<ImageView
android:id="@+id/mapviewtut4"
android:src="@drawable/mapviewtutflipper4"
android:layout_width="match_parent"
android:layout_height="wrap_content"
/>
</ViewFlipper>
</RelativeLayout>谢谢,CCJ
发布于 2011-12-14 19:02:12
这是SDK开发人员不应该担心的内部实现细节。
我见过的
其他VM实现通常允许在GC接收到要运行的信号之前将一定比例的系统内存分配给应用程序。
我相信你的话。Java的行为不是这样的。JVM并不关心存在多少系统内存--它只关心自己VM的潜在堆大小(例如-Xmx)。
然而,
扫描下面的LogCat似乎显示Dalvik至少部分地运行。
对,是这样。特别是在较新版本的Android上,GC在自己的线程中同时运行,而不是前面采用的停止世界的方法。
--这对我来说没有任何意义,因为GC最近运行了,而且没有分配任何东西,因为它应该将VM的容量控制在3MB以内(256 MB)。
您不太可能为VM提供256 VM的堆空间。取决于您的设备,它可能低至16 as。
此外,Android没有紧凑的GC算法,因此即使您有超过3MB的可用空间,也可能没有一个连续的3MB块。
这就是为什么要么recycle()您的Bitmap对象,要么尝试重用它们(例如,inBitmap of BitmapOptions,在API 11中添加)是很重要的。
此外,您还可以使用DDMS创建堆转储和垫子来检查它,更准确地确定内存的去向和谁持有什么。这在Android 3.0+上运行得更好,因为MAT能够更准确地报告Bitmap的内存。
,在虚拟机崩溃之前,这256 MB的系统内存中是否只有一小部分是分配给VM的?
是。这叫堆。Android设备有堆大小限制。通常情况下,它在16-48MB的范围内,取决于Android操作系统的版本和屏幕分辨率。
会不会是位图加载过程有自己的内存分配上限?
不,它是按照相同的堆大小预算工作的。从Android3.0开始,它实际上将内存加载到与Dalvik对象使用的其他堆相同的堆中--以前,它使用堆外的系统RAM块,但是空间是根据堆的大小预算计算的。
,但不知道究竟是什么触发了Dalvik GC,我们仍然对操作系统和Google关于性能最佳实践的模糊讨论抱有很大的信心。
就像他们说的那样,生活还在继续。
是否可以从代码中跟踪GC状态(例如“即将运行”、“运行”、“完成运行”),以便围绕可用内存战略性地规划大量资源分配?我想知道是否有一个受支持的API调用可以在生产代码(而不仅仅是调试)中依赖于跟踪垃圾收集器的精确状态。
不是的。
总是在系统范围内,或者可以分离线程(比如游戏中的专用呈现线程)来逃避GC可能造成的性能滞后问题?
对于任何VM,GC从来都不是“系统范围的”。GC总是在VM中。
在较新版本的Android上,GC是并发的,因此在正常情况下不会阻塞任何线程。在较早版本的Android上,GC是停止世界,并将影响所有线程。对于Android3.0来说,这一变化是肯定的--我的记忆是模糊的,不清楚并发GC是否已经为Android2.3设置好了。有一个关于Android内存管理的2011年Google \O演示文稿,您可能希望看到。
:这会使VM崩溃吗?还是操作系统会自动处理这些极端情况以避免VM崩溃?
在提高OutOfMemoryException之前,安卓应该强制立即启动GC。根据我以前的副翼,这个场景不符合“正常情况”。
https://stackoverflow.com/questions/8509886
复制相似问题